在Swift中支持通过navigationController?.hidesBarsOnSwipe = true
实现滚动时隐藏导航栏。
需要明确的是,我希望它只在滚动时隐藏,因此.navigationBarHidden(true)
不够用。
我尝试按照这个 Stackoverflow答案描述的方式访问NavigationController(我添加了nc.hidesBarsOnSwipe = true
),尽管编译通过,但并未起作用。
SwiftUI支持吗?
NavigationView
似乎仍然存在一些相对较严重的问题。例如,默认情况下,ScrollView
将忽略标题区域并在其下方滚动。
看起来您可以通过将 displayMode: .inline
和 StackNavigationViewStyle()
结合使用来使其正常工作。
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())
}
}
我曾经遇到过同样的问题。这是我解决它的方法。
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)
}
}