如何在 SwiftUI ScrollView 中以编程方式检测滚动方向

问题描述 投票:0回答:6

我想像 safari 一样通过滚动方向显示或隐藏项目。向上滚动时隐藏某些内容,向下滚动时显示它。

scrollview swiftui
6个回答
16
投票

我认为,simultaneousGesture是一个更好的解决方案,因为它不会阻止scrollView事件。

ScrollView {

}
.simultaneousGesture(
       DragGesture().onChanged({
           let isScrollDown = 0 < $0.translation.height
           print(isScrollDown)
       }))

此方法仅在屏幕停止滚动时检测新的滚动


15
投票

您可以使用 DragGesture 值

ScrollView {
...
}
.gesture(
   DragGesture().onChanged { value in
      if value.translation.height > 0 {
         print("Scroll down")
      } else {
         print("Scroll up")
      }
   }
)

10
投票

您可以使用 GeometryReader 获取 ScrollView 中视图中的一个的全局位置来检测滚动方向。下面的代码将打印出当前的 midY 位置。根据 +/- 值,您可以显示或隐藏其他视图。

struct ContentView: View {

var body: some View {
    ScrollView{
        GeometryReader { geometry in

                Text("Top View \(geometry.frame(in: .global).midY)")
                    .frame(width: geometry.size.width, height: 50)
                    .background(Color.orange)
            }

    }.frame(minWidth: 0, idealWidth: 0, maxWidth: .infinity, minHeight: 0, idealHeight: 0, maxHeight: .infinity, alignment: .center)
}

}


3
投票

当前的答案都不适合我,所以我使用了

PreferenceKey
更改。

经测试可在 Xcode 14.3.1 和 iOS 16.6 中工作。

@State var previousViewOffset: CGFloat = 0
let minimumOffset: CGFloat = 16 // Optional

...
ScrollView {
    VStack {
        ...
    }.background(GeometryReader {
        Color.clear.preference(key: ViewOffsetKey.self, value: -$0.frame(in: .named("scroll")).origin.y)
    }).onPreferenceChange(ViewOffsetKey.self) {
        let offsetDifference: CGFloat = abs(self.previousViewOffset - $0)

        if self.previousViewOffset > $0 {
            print("Is scrolling up toward top.")
        } else {
            print("Is scrolling down toward bottom.")
        }

        if offsetDifference > minimumOffset { // This condition is optional but the scroll direction is often too sensitive without a minimum offset.
            self.previousViewOffset = $0
        }
    }
}.coordinateSpace(name: "scroll")
...

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat
    static var defaultValue = CGFloat.zero
    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

总结一下:

  1. 您需要
    background
    修饰符及其内容。
  2. 您需要
    onPreferenceChange
    修饰符和内容。
  3. 您需要
    coordinateSpace
    修饰符。
  4. 您需要确保
    coordinateSpace
    名称与
    named
    偏好框架相匹配。
  5. 创建一个
    ViewOffsetKey
    首选项键。

1
投票

您可以像这样使用predictedEndLocation和位置

 /// A prediction, based on the current drag velocity, of where the final
 /// location will be if dragging stopped now.
 public var predictedEndLocation: CGPoint { get }


DragGesture()
        
        .onChanged({ gesture in

          if (gesture.location.y > gesture.predictedEndLocation.y){
            print("up")
          } else {
            print("down")
          }
        
    })

0
投票

我认为@Mykels 的答案是最好的,并且在 IOS16 中运行良好。不过,它的一项改进是仅在滚动量大于最小偏移量时才调用所需的函数,否则如果滚动量小于最小偏移量,最终可能会调用错误的函数。这是我的更新版本:

ScrollView(.vertical){
    LazyVStack {
    ...
    }.background(GeometryReader {
        Color.clear.preference(key: ViewOffsetKey.self, value: -$0.frame(in: .named("scroll")).origin.y)
    }).onPreferenceChange(ViewOffsetKey.self) { currentOffset in
         let offsetDifference: CGFloat = self.previousScrollOffset - currentOffset
         if ( abs(offsetDifference) > minimumOffset) {
             if offsetDifference > 0 {
                     print("Is scrolling up toward top.")
              } else {
                      print("Is scrolling down toward bottom.")
              }
              self.previousScrollOffset = currentOffset
         }
    }
}

struct ViewOffsetKey: PreferenceKey {
        typealias Value = CGFloat
        static var defaultValue = CGFloat.zero
        static func reduce(value: inout Value, nextValue: () -> Value) {
            value += nextValue()
}
© www.soinside.com 2019 - 2024. All rights reserved.