SwiftUI实现带Tab布局的可折叠头部技术求助
解决方案
问题根源
内部垂直ScrollView会优先消费拖动手势,导致外层添加的DragGesture无法接收到上下拖动的事件。
方案1:通过偏移变化直接判断滚动方向(推荐)
你已经在监听offset的变化,完全可以利用这个数据判断滚动方向,不需要额外的DragGesture:
- 先定义滚动方向枚举:
enum ScrollDirection { case up, down }
- 修改
onChange逻辑,直接通过当前偏移和上一次偏移对比判断方向:
struct ScrollingHeaderView: View { @State var progress: CGFloat = .zero @State var offset: CGPoint = .zero @State var previousOffset: CGPoint = .zero enum ScrollDirection { case up, down } var body: some View { ZStack { Color.black ScrollView(.horizontal) { LazyHStack(spacing: .zero) { OffsetObservingScrollView(axes: .vertical, offset: $offset) { // 你的滚动内容 } .onChange(of: offset) { newValue in // 判断滚动方向 let direction: ScrollDirection = newValue.y < previousOffset.y ? .up : .down print("滚动方向:\(direction)") // 原来的进度计算逻辑 let distance = newValue.y - previousOffset.y previousOffset = newValue let progress = distance / 30 self.progress = min(max(progress, 0), 1) } } } .scrollTargetBehavior(.paging) .scrollIndicators(.hidden) } } }
方案2:调整手势优先级以捕获拖动事件
如果一定要用DragGesture,需要将手势直接添加到内部垂直ScrollView上,并使用simultaneousGesture确保手势能和滚动手势同时触发:
OffsetObservingScrollView(axes: .vertical, offset: $offset) { // 你的滚动内容 } .onChange(of: offset) { newValue in // 原来的进度计算逻辑 let distance = newValue.y - previousOffset.y previousOffset = newValue let progress = distance / 30 self.progress = min(max(progress, 0), 1) } .simultaneousGesture( DragGesture(coordinateSpace: .local) .onChanged { value in let direction = value.translation.height > 0 ? .down : .up print("滚动方向:\(direction)") } )
注意这里将coordinateSpace改为.local,更贴合内部滚动视图的坐标系,避免全局坐标系带来的偏差。
额外提示
如果你的OffsetObservingScrollView是基于UIScrollView封装的,还可以直接在UIScrollViewDelegate的scrollViewDidScroll方法中判断滚动方向,这样更直接:
// 先给UIScrollView添加存储上一次偏移的属性 extension UIScrollView { private struct AssociatedKeys { static var previousOffset = "previousOffset" } var previousContentOffset: CGPoint { get { objc_getAssociatedObject(self, &AssociatedKeys.previousOffset) as? CGPoint ?? .zero } set { objc_setAssociatedObject(self, &AssociatedKeys.previousOffset, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } } // 在Delegate方法中判断方向 func scrollViewDidScroll(_ scrollView: UIScrollView) { let direction = scrollView.contentOffset.y > scrollView.previousContentOffset.y ? .down : .up // 这里可以通过闭包或绑定将方向传递给SwiftUI视图 scrollView.previousContentOffset = scrollView.contentOffset }
内容的提问来源于stack exchange,提问作者Stijnk008
相关产品推荐
相关产品推荐

