SwiftUI如何在导航栏(Navigation Bar)标题下方嵌入分段选择器(Segmented picker)并保留折叠动画
嘿,这个需求我刚好实现过,完全可以做到!下面给你分享一个适配iOS 16+的完整方案,同时也会附上iOS 15的兼容版本,保证分段选择器能完美嵌入导航栏标题下方,还能保留原生的折叠动画效果~
实现方案
核心思路
利用SwiftUI的NavigationStack(iOS 16+)或NavigationView(iOS 15及以下)的工具栏布局能力,将分段选择器放在导航标题的专属下方区域,同时通过原生的滚动折叠配置,让导航栏在滚动时自动收缩,分段选择器也会跟着同步动画,和系统搜索框的表现完全一致。
iOS 16+ 完整代码
这个版本直接利用iOS 16新增的.belowTitle工具栏位置,无需手动调整布局,代码更简洁:
import SwiftUI struct SegmentedNavView: View { // 分段选择器选项 private let segments = ["全部", "已完成", "待办"] @State private var selectedSegment = 0 var body: some View { NavigationStack { // 模拟长列表用于触发滚动折叠 ScrollView { LazyVStack(spacing: 20) { ForEach(0..<50) { index in Text("列表项 \(index + 1)") .font(.title) .frame(maxWidth: .infinity, alignment: .leading) .padding() } } } .navigationTitle("我的任务") .navigationBarTitleDisplayMode(.large) // 将分段选择器嵌入导航标题下方 .toolbar { ToolbarItem(placement: .belowTitle) { Picker("", selection: $selectedSegment) { ForEach(0..<segments.count) { index in Text(segments[index]) .tag(index) } } .pickerStyle(.segmented) .frame(width: 280) // 自定义宽度让布局更协调 } } // 开启导航栏滚动折叠动画 .toolbarScrollableAxes(.vertical) .toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.bar, for: .navigationBar) } } } struct SegmentedNavView_Previews: PreviewProvider { static var previews: some View { SegmentedNavView() } }
关键细节解释
.belowTitle位置:iOS 16专门为导航栏标题下方的内容提供了这个工具栏位置,完美匹配需求,不需要手动堆叠标题和选择器- 滚动折叠动画:
.toolbarScrollableAxes(.vertical)是开启原生导航栏折叠的关键,滚动列表时,导航栏会自动收缩,分段选择器也会同步缩小并保持在导航栏内 - 工具栏背景设置:
.toolbarBackground(.visible)确保导航栏背景始终可见,避免分段选择器滚动时被隐藏
iOS 15 兼容版本
如果需要支持iOS 15及以下,可以用NavigationView配合.principal工具栏位置实现类似效果:
import SwiftUI struct SegmentedNavViewiOS15: View { private let segments = ["全部", "已完成", "待办"] @State private var selectedSegment = 0 var body: some View { NavigationView { List(0..<50) { index in Text("列表项 \(index + 1)") .font(.title) } .navigationBarTitleDisplayMode(.large) .toolbar { ToolbarItem(placement: .principal) { // 手动堆叠标题和分段选择器 VStack(spacing: 8) { Text("我的任务") .font(.largeTitle) .fontWeight(.bold) Picker("", selection: $selectedSegment) { ForEach(0..<segments.count) { index in Text(segments[index]) .tag(index) } } .pickerStyle(.segmented) .frame(width: 280) } } } .toolbarBackground(.visible, for: .navigationBar) .toolbarBackground(.bar, for: .navigationBar) } } }
注意事项
- 可以根据需求调整分段选择器的宽度、颜色等样式,让它和你的App风格统一
- 如果需要自定义导航栏背景色,只需要修改
.toolbarBackground的参数即可,比如.toolbarBackground(Color.blue, for: .navigationBar) - 测试时要确保滚动列表足够长,才能触发导航栏的折叠动画
内容的提问来源于stack exchange,提问作者Just A Question
相关产品推荐
相关产品推荐

