admin管理员组文章数量:1410697
I have a List that contains some views, some of which need horizontal scrolling. It is important to note that none of the views require vertical scrolling. When my mouse hovers over the views that do not require horizontal scrolling, the List works normally and can scroll vertically. However, when my mouse hovers over the views that require horizontal scrolling for a moment, I am unable to scroll the List vertically; I can only scroll horizontally. If I replace the List with a ScrollView, everything works perfectly, and both scrollings function well. However, ScrollView(including LazyVSatck) has serious performance issues in my App, so I had to turn to List. Although macOS 13+ provides .scrollDisabled(true)
to disable scrolling, it is effective, but it disables both horizontal and vertical scrolling simultaneously, which is not what I want. The last part of the video shows my attempt to scroll vertically, which does not work.
Example video
Here is the minimal reproducible code.
import SwiftUI
struct ContentView: View {
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index) line")
}
ScrollView(.horizontal) {
Text("hello, world! hello, world! hello, world! hello, world! hello, world!\n hello, world! hello, world! hello, world! \n hello, world! hello, world! hello, world! hello, world!")
}.font(.largeTitle)
ForEach(5..<10) { index in
Text("\(index) line")
}
}
}
}
#Preview {
ContentView()
}
I expecting the List to receive vertical scrolling events no matter what.
I have a List that contains some views, some of which need horizontal scrolling. It is important to note that none of the views require vertical scrolling. When my mouse hovers over the views that do not require horizontal scrolling, the List works normally and can scroll vertically. However, when my mouse hovers over the views that require horizontal scrolling for a moment, I am unable to scroll the List vertically; I can only scroll horizontally. If I replace the List with a ScrollView, everything works perfectly, and both scrollings function well. However, ScrollView(including LazyVSatck) has serious performance issues in my App, so I had to turn to List. Although macOS 13+ provides .scrollDisabled(true)
to disable scrolling, it is effective, but it disables both horizontal and vertical scrolling simultaneously, which is not what I want. The last part of the video shows my attempt to scroll vertically, which does not work.
Example video
Here is the minimal reproducible code.
import SwiftUI
struct ContentView: View {
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index) line")
}
ScrollView(.horizontal) {
Text("hello, world! hello, world! hello, world! hello, world! hello, world!\n hello, world! hello, world! hello, world! \n hello, world! hello, world! hello, world! hello, world!")
}.font(.largeTitle)
ForEach(5..<10) { index in
Text("\(index) line")
}
}
}
}
#Preview {
ContentView()
}
I expecting the List to receive vertical scrolling events no matter what.
Share edited Mar 7 at 16:34 Binglei Ma asked Mar 7 at 15:37 Binglei MaBinglei Ma 214 bronze badges 2- Seems related to this one which has some workarounds: stackoverflow/questions/64920744/… – drseg Commented Mar 8 at 16:19
- Thank you @drseg, I got inspiration from the link you provided and also referenced this post. – Binglei Ma Commented Mar 15 at 13:59
1 Answer
Reset to default 1By referring to this post and this post, I got my workaround.
Although my issue has been resolved, I am still waiting for a native SwiftUI solution. This is just a workaround.
1. First, based on this answer, I need to implement my own NSScrollView()
and override the scrollWheel()
method to forward vertical scroll events. This answer also forwards vertical scroll events by overriding wantsForwardedScrollEvents()
, but it is "too sensitive". Users' fingers cannot scroll precisely horizontally; there will always be a certain distance generated on the y-axis. Therefore, I did not adopt that method, even though it seems to be "less intrusive".
class MTHorizontalScrollView: NSScrollView {
var currentScrollIsHorizontal = false
override func scrollWheel(with event: NSEvent) {
if event.phase == NSEvent.Phase.began || (event.phase == NSEvent.Phase.ended && event.momentumPhase == NSEvent.Phase.ended) {
currentScrollIsHorizontal = abs(event.scrollingDeltaX) > abs(event.scrollingDeltaY)
}
if currentScrollIsHorizontal {
super.scrollWheel(with: event)
} else {
self.nextResponder?.scrollWheel(with: event)
}
}
}
2. I need to create an NSViewRepresentable
to use it in SwiftUI.
struct NSScrollViewWrapper<Content: View>: NSViewRepresentable {
let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
func makeNSView(context: Context) -> NSScrollView {
let scrollView = MTHorizontalScrollView()
scrollView.hasHorizontalScroller = true
scrollView.hasVerticalScroller = false
scrollView.verticalScrollElasticity = .none
scrollView.horizontalScrollElasticity = .allowed
scrollView.autohidesScrollers = true
scrollView.drawsBackground = false
let hostingView = NSHostingView(rootView: content)
hostingView.translatesAutoresizingMaskIntoConstraints = false
scrollView.documentView = hostingView
NSLayoutConstraint.activate([
hostingView.topAnchor.constraint(equalTo: scrollView.topAnchor),
hostingView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
hostingView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
hostingView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor)
])
return scrollView
}
func updateNSView(_ nsView: NSScrollView, context: Context) {
if let hostingView = nsView.documentView as? NSHostingView<Content> {
hostingView.rootView = content
}
}
}
3. Perhaps completing step 2 is sufficient for use, but here I will take it a step further and extend it to View
for easier use anywhere.
extension View {
@ViewBuilder
func forwardedScrollEvents(_ enabled: Bool = true) -> some View {
if enabled {
NSScrollViewWrapper {
self
.scrollDisabled(true)
.frame(maxWidth: .infinity, alignment: .leading)
}
} else {
self
}
}
}
4. Everything is ready, and it can be used now.
struct ContentView: View {
var body: some View {
List {
ForEach(0..<5) { index in
Text("\(index) line")
}
ScrollView(.horizontal) {
Text("hello, world! hello, world! hello, world! hello, world! hello, world!\n hello, world! hello, world! hello, world! \n hello, world! hello, world! hello, world! hello, world!")
}.font(.largeTitle)
.forwardedScrollEvents() //this!
ForEach(5..<10) { index in
Text("\(index) line")
}
}
}
}
本文标签:
版权声明:本文标题:swift - List cannot scroll vertically If hovered a List subview that needs to support horizontal scrolling - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744920207a2632247.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论