【问题标题】:Get Scroll Direction of List in SwiftUI在 SwiftUI 中获取列表的滚动方向
【发布时间】:2021-08-31 13:59:34
【问题描述】:

我在 SwiftUI 视图中有一个列表。在 UIHostingController 中。我想根据列表的滚动方向隐藏或显示 UINavigationBar。这个 UINavigationBar 在 UIHostingController 的 UIkit 中。 我尝试添加 DragGesture,但它没有提供滚动方向的持续更新。

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView.panGestureRecognizer.translation(in: scrollView).y < 0 {
            navigationController?.setNavigationBarHidden(true, animated: true)
        } else {
            navigationController?.setNavigationBarHidden(false, animated: true)
        }
    }

基本上,我需要在 SwiftUI 中替换上述代码。

请不要推荐 LazyList 或任何与 iOS 14 相关的解决方案,因为我的最低 iOS 目标是 iOS 13。

【问题讨论】:

  • 添加一些额外的信息,看看这可能对你有帮助:How to make a SwiftUI List scroll automatically?
  • 你有什么理由使用 UIKit 吗?在纯 SwiftUI 中使用自定义 ScrollView 怎么样?
  • @mahan 我只为 UInavigationBar 使用 uikit,因为我们在整个应用程序中都有通用的导航栏。我只需要知道 Swiftui 本身中列表的滚动方向。
  • 这是否回答了您的问题stackoverflow.com/a/63216812/12299030

标签: ios swift swiftui swiftui-list


【解决方案1】:

您可以创建 ScrollViewReader 观察偏好键的变化。

struct DemoScrollViewOffsetView: View {

  @State private var offset = CGFloat.zero

  var body: some View {
      ScrollView {
          VStack {
              ForEach(0..<100) { i in
                  Text("Item \(i)").padding()
              }
          }.background(GeometryReader {
              Color.clear.preference(key: ViewOffsetKey.self,
                                   value: -$0.frame(in: .named("scroll")).origin.y)
          }).onPreferenceChange(ViewOffsetKey.self) { print("offset >> \($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()
    }
}

这将读取您的内容偏移量并打印出来。

【讨论】:

  • 我知道这一点,但是我将如何通过偏移量确定滚动方向。
  • 在您的情况下,偏移量将相当于scrollView.panGestureRecognizer.translation(in: scrollView).y
  • 不,不是因为平移和偏移不同。我必须对上述逻辑做一些解决方法。我确实找到了解决方案。但是由于使用geometryreader,滚动视图变得滞后。需要找到解决办法。
  • @Prathamesh 您可以分享您使用geometryReader 所做的解决方案吗?也许我可以帮助解决滞后问题。
【解决方案2】:

我认为解决这个问题最简单的方法是使用Introspect

可以用KVO观察table view内容的偏移量,不需要继承。

我尝试使用State 变量为navigationBarHidden 设置动画,但它在没有动画的情况下会发生某种变化。问题类似于this issue,但建议的解决方案对我不起作用,所以我不得不使用自省导航控制器来解决。

我把这个逻辑移到了修饰符上:

import SwiftUI
import Introspect

extension View {
    func hideNavigationBarOnScroll() -> some View {
        modifier(HideNavigationBarOnScrollModifier())
    }
}

private struct HideNavigationBarOnScrollModifier: ViewModifier {
    @State
    var observation: NSKeyValueObservation?

    @State
    var navigationController: UINavigationController?
    
    func body(content: Content) -> some View {
        content
            .introspectNavigationController {
                navigationController = $0
            }
            .introspectTableView { tableView in
                observation = tableView.observe(\.contentOffset) { tableView, change in
                    navigationController?.setNavigationBarHidden(
                        tableView.panGestureRecognizer.translation(in: tableView).y < 0,
                        animated: true
                    )
                }
            }
    }
}

用法:

struct ContentView: View {
    let list = (0..<100).map { _ in
        UUID().uuidString
    }

    var body: some View {
        NavigationView {
            List(list, id: \.self) { item in
                Text(item)
            }
                .hideNavigationBarOnScroll()
                .navigationBarTitle("Title")
        }
    }
}

【讨论】:

    【解决方案3】:

    我在这里对您的要求进行了一些研究,并在 Medium 上发现了 Mos6y 的这篇有趣的文章。 Swift/iOS: Determine Scroll Direction

    var lastVelocityYSign = 0
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        let currentVelocityY =  scrollView.panGestureRecognizer.velocity(in: scrollView.superview).y
        let currentVelocityYSign = Int(currentVelocityY).signum()
        if currentVelocityYSign != lastVelocityYSign &&
            currentVelocityYSign != 0 {
            lastVelocityYSign = currentVelocityYSign
        }
        if lastVelocityYSign < 0 {
            print("SCROLLING DOWN")
        } else if lastVelocityYSign > 0 {
            print("SCOLLING UP")
        }
    }
    

    【讨论】:

    • 这是一个 UIKit 解决方案
    • 回答前请阅读问题
    猜你喜欢
    • 2020-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-02
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    相关资源
    最近更新 更多