macOS下SwiftUI嵌套ScrollView水平滚动卡顿问题求助
macOS SwiftUI 嵌套ScrollView滚动异常解决方案
针对水平ScrollView嵌套垂直ScrollView时,macOS下水平滚动鼠标下方内容冻结的问题,以下是几个可行的解决方案:
1. 手动控制内层ScrollView的手势响应
通过onHover和手势修饰符,在鼠标悬停内层垂直ScrollView时,临时让外层水平ScrollView优先处理滚动事件:
@State private var enableInnerScroll = true ScrollView(.horizontal) { HStack(spacing: 20) { ForEach(0..<10) { index in ScrollView(.vertical) { VStack(spacing: 10) { ForEach(0..<20) { row in Text("Item \(index)-\(row)") .frame(width: 200, height: 50) .background(Color.gray.opacity(0.2)) } } } .onHover { isHovered in enableInnerScroll = isHovered } .gesture( DragGesture() .onChanged { _ in guard enableInnerScroll else { return } // 仅当允许内层滚动时才处理手势 } ) } } }
2. 用AppKit的NSScrollView替代内层SwiftUI ScrollView
利用SwiftUI与AppKit的互操作,用原生NSScrollView实现垂直滚动,避免手势冲突:
struct AppKitVerticalScroll<Content: View>: NSViewRepresentable { let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } func makeNSView(context: Context) -> NSScrollView { let scrollView = NSScrollView() scrollView.hasVerticalScroller = true scrollView.hasHorizontalScroller = false scrollView.autohidesScrollers = true scrollView.documentView = NSHostingView(rootView: content) return scrollView } func updateNSView(_ nsView: NSScrollView, context: Context) { (nsView.documentView as? NSHostingView<Content>)?.rootView = content } } // 使用示例 ScrollView(.horizontal) { HStack(spacing: 20) { ForEach(0..<10) { index in AppKitVerticalScroll { VStack(spacing: 10) { ForEach(0..<20) { row in Text("Item \(index)-\(row)") .frame(width: 200, height: 50) .background(Color.gray.opacity(0.2)) } } } .frame(width: 200) } } }
3. 区分ScrollView的坐标空间
给外层和内层ScrollView指定独立的命名坐标空间,帮助SwiftUI正确识别手势的目标视图:
ScrollView(.horizontal, coordinateSpace: .named("OuterHorizontal")) { HStack(spacing: 20) { ForEach(0..<10) { index in ScrollView(.vertical, coordinateSpace: .named("InnerVertical-\(index)")) { VStack(spacing: 10) { ForEach(0..<20) { row in Text("Item \(index)-\(row)") .frame(width: 200, height: 50) .background(Color.gray.opacity(0.2)) } } } .frame(width: 200) } } }
4. 滚动时动态禁用内层ScrollView
检测水平滚动状态,在滚动过程中禁用内层垂直ScrollView,滚动结束后恢复:
@State private var isHorizontalScrolling = false ScrollView(.horizontal) { HStack(spacing: 20) { ForEach(0..<10) { index in ScrollView(.vertical) { VStack(spacing: 10) { ForEach(0..<20) { row in Text("Item \(index)-\(row)") .frame(width: 200, height: 50) .background(Color.gray.opacity(0.2)) } } } .disabled(isHorizontalScrolling) .frame(width: 200) } } } .gesture( DragGesture(minimumDistance: 5) .onChanged { value in // 判断滚动方向,优先响应水平滚动 if abs(value.translation.width) > abs(value.translation.height) { isHorizontalScrolling = true } } .onEnded { _ in isHorizontalScrolling = false } )
内容的提问来源于stack exchange,提问作者Max Gribov
相关产品推荐
相关产品推荐

