SwiftUI:如何让导航栏标题及按钮随滚动时导航栏显隐?
实现导航栏标题/按钮随滚动动态显示
完全可以实现这个需求,下面分两种场景给出具体实现方案:
方案一:iOS 17+(推荐)
利用iOS 17新增的scrollPosition API直接跟踪列表滚动位置,逻辑更简洁:
struct ContentView: View { @State private var showNavElements = false @State private var scrollPosition: Int? var body: some View { TabView { NavigationStack { List { // 头部视图,设置唯一ID用于滚动跟踪 HStack { Spacer() Text("Header") Spacer() } .frame(height: 300) .background(.red) .listRowInsets(EdgeInsets()) .id(-1) // 列表项 ForEach(1..<100) { index in Text("\(index)") .id(index) } } .listStyle(.plain) .ignoresSafeArea(.all, edges: .top) // 绑定滚动位置 .scrollPosition(id: $scrollPosition) // 根据滚动位置切换导航元素显示状态 .onChange(of: scrollPosition) { _, newPos in showNavElements = newPos != nil && newPos! >= 1 } // 动态显示/隐藏导航标题 .navigationTitle(showNavElements ? "Navigation Title" : "") .navigationBarTitleDisplayMode(.inline) // 控制导航栏背景显示 .toolbarBackground(showNavElements ? .visible : .hidden, for: .navigationBar) .toolbarBackground(.white, for: .navigationBar) // 动态显示导航按钮 .toolbar { if showNavElements { Button("设置") { // 按钮逻辑 } } } } } } }
逻辑说明
- 给头部视图和每个列表项设置唯一
id,通过scrollPosition跟踪当前滚动到的元素ID - 当滚动到第一个列表项(
index≥1)时,触发showNavElements为true,此时导航栏的标题、背景和按钮自动显示;滚动回头部时自动隐藏
方案二:兼容iOS 16及以下
对于低版本系统,用GeometryReader结合自定义PreferenceKey监听滚动偏移量:
struct ContentView: View { @State private var showNavElements = false var body: some View { TabView { NavigationStack { List { HStack { Spacer() Text("Header") Spacer() } .frame(height: 300) .background(.red) .listRowInsets(EdgeInsets()) ForEach(1..<100) { index in Text("\(index)") } } .listStyle(.plain) .ignoresSafeArea(.all, edges: .top) // 监听滚动偏移 .overlay( GeometryReader { proxy in Color.clear .preference(key: ScrollOffsetKey.self, value: proxy.frame(in: .named("scroll")).minY) } ) .coordinateSpace(name: "scroll") // 根据偏移量切换显示状态 .onPreferenceChange(ScrollOffsetKey.self) { offset in // 阈值可根据头部高度调整,这里设为-200表示滚动超过头部下方200pt时显示 showNavElements = offset < -200 } .navigationTitle(showNavElements ? "Navigation Title" : "") .navigationBarTitleDisplayMode(.inline) .toolbarBackground(showNavElements ? .visible : .hidden, for: .navigationBar) .toolbar { if showNavElements { Button("设置") {} } } } } } } // 自定义PreferenceKey用于传递滚动偏移量 struct ScrollOffsetKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } }
逻辑说明
- 通过
GeometryReader获取列表在自定义坐标空间内的偏移量,用PreferenceKey传递给视图 - 当偏移量小于设定阈值(比如-200)时,判断用户已经滚动到头部下方,触发导航元素显示
内容的提问来源于stack exchange,提问作者Berry Blue
相关产品推荐
相关产品推荐

