如何在SwiftUI中滚动时隐藏导航栏?

9

在Swift中支持通过navigationController?.hidesBarsOnSwipe = true实现滚动时隐藏导航栏。

需要明确的是,我希望它只在滚动时隐藏,因此.navigationBarHidden(true)不够用。

我尝试按照这个 Stackoverflow答案描述的方式访问NavigationController(我添加了nc.hidesBarsOnSwipe = true),尽管编译通过,但并未起作用。

SwiftUI支持吗?

2个回答

3

NavigationView 似乎仍然存在一些相对较严重的问题。例如,默认情况下,ScrollView 将忽略标题区域并在其下方滚动。

看起来您可以通过将 displayMode: .inlineStackNavigationViewStyle() 结合使用来使其正常工作。

struct ContentView: View {
    var body: some View {
        NavigationView {
            ScrollView {
                ForEach(0...20, id: \.self) { count in
                    (count % 2 == 0 ? Color.red : Color.blue)
                        .frame(height: 44.0)
                }
            }
            .background(NavigationConfigurator { nc in // NavigationConfigurator is from the OP's post: https://dev59.com/C1MI5IYBdhLWcg3wUp02#58427754
                nc.hidesBarsOnSwipe = true
            })
            .navigationBarTitle("Hello World", displayMode: .inline)
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

滚动前: 滚动前

滚动后: 滚动后


迫不及待地想要尝试一下这个。 - elight
2
它不起作用。还有其他解决方案吗? - Nijar
例如,默认情况下,ScrollView 将忽略标题区域并在其下方滚动。这不是故意的吗?我认为这是为了实现亚克力效果。 - Lakshith Nishshanke

1

我曾经遇到过同样的问题。这是我解决它的方法。

  1. 获取视图的滚动偏移量
  2. 根据偏移量隐藏或显示导航栏

1. 获取滚动位置

请参见此处了解如何执行此操作。我将在此处添加示例代码。

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

import SwiftUI

struct ObservableScrollView<Content>: View where Content : View {
    @Namespace var scrollSpace
    @Binding var scrollOffset: CGFloat
    let content: () -> Content
    
    init(scrollOffset: Binding<CGFloat>,
         @ViewBuilder content: @escaping () -> Content) {
        _scrollOffset = scrollOffset
        self.content = content
    }
    
    var body: some View {
        ScrollView {
                content()
                    .background(GeometryReader { geo in
                        let offset = -geo.frame(in: .named(scrollSpace)).minY
                        Color.clear
                            .preference(key: ScrollViewOffsetPreferenceKey.self,
                                        value: offset)
                    })
            
        }
        .coordinateSpace(name: scrollSpace)
        .onPreferenceChange(ScrollViewOffsetPreferenceKey.self) { value in
            scrollOffset = value
        }
    }
}

2. 根据偏移量隐藏或显示导航栏

现在我们可以使用上面创建的可观察滚动视图。

@State var scrollOffset: CGFloat = CGFloat.zero
@State var hideNavigationBar: Bool = false

var body: some View {
        NavigationView {
            ObservableScrollView(scrollOffset: self.$scrollOffset) {
                Text("I'm observable")
            }
            .navigationTitle("Title")
            .onChange(of: scrollOffset, perform: { scrollOfset in
                let offset = scrollOfset + (self.hideNavigationBar ? 50 : 0) // note 1
                if offset > 60 { // note 2
                    withAnimation(.easeIn(duration: 1), {
                        self.hideNavigationBar = true
                    })
                }
                if offset < 50 {
                    withAnimation(.easeIn(duration: 1), {
                        self.hideNavigationBar = false
                    })
                }
            })
            .navigationBarHidden(hideNavigationBar)
        }
    }

注意1:假设导航标题的高度为50。(这将根据样式而改变。)当导航栏消失时,滚动偏移立即下降该高度。如果隐藏了导航栏,请将导航栏的高度添加到偏移量中以保持偏移量一致。
注意2:我故意让隐藏和显示的两个阈值之间存在一个小差异,而不是使用相同的值,因为如果用户滚动并将其保持在阈值内,它不会闪烁。

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