在SwiftUI中获取列表的滚动方向

3

我在SwiftUI View中有一个列表,它位于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。


添加一些额外信息,也许可以帮助您:如何使SwiftUI列表自动滚动? - Anbu.Karthik
您使用UIKit的原因是什么?在纯SwiftUI中使用自定义ScrollView怎么样? - mahan
@mahan 我只是为了整个应用程序中的通用导航栏而使用uikit中的UInavigationBar。我只需要知道SwiftUI列表的滚动方向即可。 - Prathamesh
这个回答解决了你的问题吗?https://dev59.com/_1EHtIcB2Jgan1zng10x#63216812 - Asperi
3个回答

4

您可以创建 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()
    }
}

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


我知道这个,但是如何通过偏移量确定滚动方向呢? - Prathamesh
在您的情况下,偏移量将等同于 scrollView.panGestureRecognizer.translation(in: scrollView).y - kchopda
不是很行,因为翻译和偏移量是不同的。我必须在上述逻辑中进行一些变通。我找到了解决方案。但是由于使用geometryreader,滚动视图变得很卡顿。需要找到解决方法。 - Prathamesh
@Prathamesh,你能分享一下使用GeometryReader解决问题的方法吗?也许我可以帮忙解决滞后问题。 - kchopda

1

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

您可以通过KVO观察表视图内容的偏移量,无需继承。

我尝试使用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")
        }
    }
}

-2

我稍微调研了一下您的要求,并在Medium上找到了这篇Mos6y有趣的文章。Swift/iOS: 确定滚动方向

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解决方案。 - jnpdx
请在回答前阅读问题。 - Prathamesh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接