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

Can*_*ndy 9 scrollview swiftui

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

Mar*_* T. 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)
}
Run Code Online (Sandbox Code Playgroud)

}

  • 这看起来很有希望,并且可以避免 onChange 被触发一次。如何使用 midY 值来检测方向?或者设置一个状态变量? (5认同)

小智 8

您可以使用 DragGesture 值

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

  • 不幸的是,将“DragGesture”应用于“ScrollView”将导​​致“onChanged”回调仅被触发一次,因为滚动视图默认处理这些事件。因此,这不会产生正确的行为。 (15认同)

Den*_*Den 5

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

ScrollView {

}
.simultaneousGesture(
       DragGesture().onChanged({
           let isScrollDown = 0 < $0.translation.height
           print(isScrollDown)
       }))
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,将“DragGesture”应用于“ScrollView”将导​​致“onChanged”回调仅被触发一次,因为滚动视图默认处理这些事件。因此,这不会产生正确的行为。 (9认同)

Myk*_*kel 5

目前的答案都不适合我,所以我使用了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()
    }
}

Run Code Online (Sandbox Code Playgroud)

总结一下:

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