struct ContentView: View { var body: some View { TabView { Text("首页内容") .tabItem { Label("首页", systemImage: "house") } Text("设置页面") .tabItem { Label("设置", systemImage: "gear") } } } }就这么几行代码,一个基本的双标签界面就出来了!在 iOS 16 之后,你还可以使用更简洁的语法:
TabView { Tab("首页", systemImage: "house") { HomeView() } Tab("设置", systemImage: "gear") { SettingsView() } }
TabView { HomeView() .tabItem { Label("首页", systemImage: "house") } CartView() .tabItem { Label("购物车", systemImage: "cart") // 堆代码 duidaima.com } .badge(3) // 显示数字 3 ProfileView() .tabItem { Label("我的", systemImage: "person") } .badge("!") // 显示感叹号 }你可以用数字展示具体的未读消息数量,也可以用文本符号(比如"!")表示需要用户注意的事项。在实际项目中,我通常会把徽章跟实际的数据源绑定起来:
.badge(cartItems.isEmpty ? nil : cartItems.count)这样当购物车为空时不显示徽章,有商品时就显示具体数量,简直完美!
struct MainTabView: View { // 使用 @State 追踪当前选中的标签 @Stateprivatevar selectedTab = 0 var body: some View { TabView(selection: $selectedTab) { HomeView() .tabItem { Label("首页", systemImage: "house") } .tag(0) ExploreView() .tabItem { Label("发现", systemImage: "safari") } .tag(1) ProfileView() .tabItem { Label("我的", systemImage: "person") } .tag(2) } // 使用 toolbar 添加一个测试按钮 .toolbar { Button("跳转到发现页") { withAnimation { selectedTab = 1// 切换到"发现"标签 } } } } }关键在于两点:
2.使用 tag() 修饰符给每个标签设置一个唯一标识
3.将状态变量通过 selection 参数绑定到 TabView
3.根据不同的用户角色显示不同的初始标签
struct CustomizableTabView: View { // 使用 @AppStorage 持久化用户的自定义设置 @AppStorage("tab-view-customization") privatevar customization: TabViewCustomization var body: some View { TabView { HomeTab() .tabItem { Label("首页", systemImage: "house") } MessageTab() .tabItem { Label("消息", systemImage: "message") } .customizationID("com.myapp.tab.messages") SettingsTab() .tabItem { Label("设置", systemImage: "gear") } .customizationID("com.myapp.tab.settings") } .tabViewStyle(.sidebarAdaptable) .tabViewCustomization($customization) } }要启用这个功能,需要做这几件事:
3.添加 .tabViewCustomization() 修饰符并关联一个存储设置的变量
TabView { // 基础标签 HomeTab() .tabItem { Label("首页", systemImage: "house") } // 内容相关标签分组 TabSection("内容中心") { ArticlesTab() .tabItem { Label("文章", systemImage: "doc.text") } VideosTab() .tabItem { Label("视频", systemImage: "play.rectangle") } PodcastsTab() .tabItem { Label("播客", systemImage: "headphones") } } // 个人相关标签分组 TabSection("个人中心") { ProfileTab() .tabItem { Label("资料", systemImage: "person") } SettingsTab() .tabItem { Label("设置", systemImage: "gear") } } } .tabViewStyle(.sidebarAdaptable)这种分组方式在 iPad 上特别有用——它会在侧边栏中形成层级结构,让导航更加清晰。而在 iPhone 上,它们仍然会以普通标签的形式显示在标签栏中。
TabSection("消息中心") { InboxTab() .tabItem { Label("收件箱", systemImage: "tray.and.arrow.down") } SentTab() .tabItem { Label("已发送", systemImage: "tray.and.arrow.up") } } .sectionActions { Button("新建消息") { // 创建新消息的操作 } Button("标记全部已读") { // 标记全部已读的操作 } }这些操作按钮会显示在分组的标题旁边,为用户提供与该分组相关的快捷功能。
struct SocialAppTabView: View { @StateObjectprivatevar notificationCenter = NotificationCenter() @Stateprivatevar selection = 0 @AppStorage("tab-customization") privatevar customization: TabViewCustomization var body: some View { TabView(selection: $selection) { // 首页标签 NavigationView { HomeView(onMessageTap: { goToMessages() }) .navigationTitle("首页") } .tabItem { Label("首页", systemImage: "house") } .tag(0) // 发现内容分组 TabSection("发现") { NavigationView { TrendingView() .navigationTitle("热门") } .tabItem { Label("热门", systemImage: "flame") } .tag(1) NavigationView { ExploreView() .navigationTitle("探索") } .tabItem { Label("探索", systemImage: "safari") } .tag(2) } .customizationID("com.myapp.section.discover") // 消息标签 NavigationView { MessageListView() .navigationTitle("消息") } .tabItem { Label("消息", systemImage: "message") } .badge(notificationCenter.unreadMessageCount > 0 ? notificationCenter.unreadMessageCount : nil) .tag(3) .customizationID("com.myapp.tab.messages") // 个人标签 NavigationView { ProfileView() .navigationTitle("我的") } .tabItem { Label("我的", systemImage: "person") } .tag(4) .customizationID("com.myapp.tab.profile") } .tabViewStyle(.sidebarAdaptable) .tabViewCustomization($customization) .onReceive(notificationCenter.$newMessageReceived) { received in if received { // 收到新消息时提示用户 if selection != 3 { // 如果当前不在消息标签 // 这里可以显示一个临时通知 } } } } privatefunc goToMessages() { withAnimation { selection = 3// 跳转到消息标签 } } } // 模拟的通知中心 class NotificationCenter: ObservableObject { @Publishedvar unreadMessageCount: Int = 0 @Publishedvar newMessageReceived: Bool = false init() { // 模拟接收新消息 Timer.scheduledTimer(withTimeInterval: 30, repeats: true) { [weakself] _in guardletself = selfelse { return } self.unreadMessageCount += 1 self.newMessageReceived = true // 堆代码 duidaima.com // 3秒后重置新消息标志 DispatchQueue.main.asyncAfter(deadline: .now() + 3) { self.newMessageReceived = false } } } }这个例子实现了多种高级功能:
.程序化导航(从首页跳转到消息页)
.响应实时通知的逻辑
struct OptimizedTabView: View { @Stateprivatevar selectedTab = 0 var body: some View { TabView(selection: $selectedTab) { LazyView(HomeView()) .tabItem { Label("首页", systemImage: "house") } .tag(0) // 其他标签使用 LazyView 包装 } } } // 懒加载包装器 struct LazyView<Content: View>: View { let build: () -> Content init(_ build: @autoclosure @escaping () -> Content) { self.build = build } var body: Content { build() } }这个技巧在有大量标签或标签内容复杂的应用中特别有用。
TabView { // 标签内容 } .ignoresSafeArea(.keyboard) // iOS 14+另一种方法是在键盘出现时隐藏标签栏:
struct KeyboardAwareTabView: View { @Stateprivatevar keyboardVisible = false var body: some View { TabView { // 标签内容 } .opacity(keyboardVisible ? 0 : 1) .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _in withAnimation { keyboardVisible = true } } .onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _in withAnimation { keyboardVisible = false } } } }3. 处理深色模式下的标签图标
TabView { HomeView() .tabItem { Image(systemName: "house") .environment(\.symbolVariants, .none) // 使用填充样式 Text("首页") } } .accentColor(.orange) // 自定义选中颜色4. 在 iPad 上适配侧边栏
TabView { // 标签内容 } .tabViewStyle(DeviceType.isPad ? .sidebar : .automatic) // 设备类型检测 enum DeviceType { static var isPad: Bool { UIDevice.current.userInterfaceIdiom == .pad } }总结