SwiftUI TabView 源码解析

SwiftUI TabView 解析

TabView 是 SwiftUI 中用于创建标签页视图的组件,类似于 UIKit 中的 UITabBarController。下面我将解释源代码并提供使用示例。

源代码解析

基本结构

swift 复制代码
public struct TabView<SelectionValue, Content> : View 
    where SelectionValue : Hashable, Content : View
  • TabView 是一个泛型结构体,遵循 View 协议
  • SelectionValue 是用于跟踪当前选中标签的值的类型,必须遵循 Hashable 协议
  • Content 是视图内容的类型,必须遵循 View 协议

初始化方法

  1. 旧版初始化方法 (已弃用)
swift 复制代码
@available(iOS, deprecated: 100000.0, message: "Use TabContentBuilder-based TabView initializers instead")
public init(selection: Binding<SelectionValue>?, @ViewBuilder content: () -> Content)
  • 这个初始化方法已被标记为弃用
  • 使用 @ViewBuilder 构建内容
  • 接受一个可选的 Binding<SelectionValue> 用于跟踪当前选中的标签

使用示例

swift 复制代码
import SwiftUI

struct OldTabViewExample: View {
    @State private var selectedTab: Int = 0

    var body: some View {
        TabView(selection: $selectedTab) { // selection 是可选 Binding
            HomeView()
                .tabItem { Label("首页", systemImage: "house.fill") }
                .tag(0)

            CalendarView()
                .tabItem { Label("日历", systemImage: "calendar") }
                .tag(1)

            SettingsView()
                .tabItem { Label("设置", systemImage: "gearshape") }
                .tag(2)
        }
    }
}
  1. 新版初始化方法 (iOS 18+)
swift 复制代码
@available(iOS 18.0, macOS 15.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
public init<C>(selection: Binding<SelectionValue>, @TabContentBuilder<SelectionValue> content: () -> C) 
    where Content == TabContentBuilder<SelectionValue>.Content<C>, C : TabContent
  • 使用新的 @TabContentBuilder 构建内容
  • 需要 iOS 18+ 等新系统版本
  • 提供更好的类型安全性和更简洁的语法

使用示例

swift 复制代码
import SwiftUI

struct NewTabViewExample: View {
    @State private var selectedTab: Int = 0

    var body: some View {
        if #available(iOS 18.0, *) {
            TabView(selection: $selectedTab) { // 必须有 Binding
                Tab(value: 0) {
                    HomeView()
                }
                .tabItem { Label("首页", systemImage: "house.fill") }

                Tab(value: 1) {
                    CalendarView()
                }
                .tabItem { Label("日历", systemImage: "calendar") }

                Tab(value: 2) {
                    SettingsView()
                }
                .tabItem { Label("设置", systemImage: "gearshape") }
            }
        } else {
            Text("请升级到 iOS 18 使用该写法")
        }
    }
}

扩展方法

  1. 当 SelectionValue 为 Int 时的简化初始化
swift 复制代码
extension TabView where SelectionValue == Int {
    nonisolated public init(@ViewBuilder content: () -> Content)
}
  • 当标签选择值是 Int 类型时,可以省略 selection 参数

这也就是最常见的使用方法了

示例

swift 复制代码
import SwiftUI

struct IntTabViewExample: View {
    var body: some View {
        TabView { // 没有 selection
            HomeView()
                .tabItem { Label("首页", systemImage: "house.fill") }
                .tag(0)

            CalendarView()
                .tabItem { Label("日历", systemImage: "calendar") }
                .tag(1)

            SettingsView()
                .tabItem { Label("设置", systemImage: "gearshape") }
                .tag(2)
        }
    }
}
  1. 当 SelectionValue 为 Never 时的初始化 (iOS 18+)
swift 复制代码
extension TabView {
    public init<C>(@TabContentBuilder<Never> content: () -> C) 
        where SelectionValue == Never, Content == TabContentBuilder<Never>.Content<C>, C : TabContent
}
  • 用于不需要选择功能的标签视图
  • 需要 iOS 18+ 等新系统版本

示例

swift 复制代码
struct StaticTabView: View {
    var body: some View {
        TabView {
            Tab("Home") {
                Text("Home Content")
            }
            
            Tab("About") {
                Text("About Content")
            }
        }
    }
}

更多的使用示例

基础示例 (iOS 13+ 兼容)

swift 复制代码
struct BasicTabView: View {
    @State private var selectedTab = 0
    
    var body: some View {
        TabView(selection: $selectedTab) {
            Text("First Tab")
                .tabItem {
                    Image(systemName: "1.square.fill")
                    Text("First")
                }
                .tag(0)
            
            Text("Second Tab")
                .tabItem {
                    Image(systemName: "2.square.fill")
                    Text("Second")
                }
                .tag(1)
        }
    }
}

简化版 (当 SelectionValue 是 Int)

swift 复制代码
struct SimpleTabView: View {
    var body: some View {
        TabView {
            Text("Home")
                .tabItem {
                    Label("Home", systemImage: "house")
                }
            
            Text("Settings")
                .tabItem {
                    Label("Settings", systemImage: "gear")
                }
        }
    }
}

iOS 18+ 新 API 示例

swift 复制代码
@available(iOS 18.0, macOS 15.0, *)
struct NewTabViewExample: View {
    @State private var selectedTab: String = "home"
    
    var body: some View {
        TabView(selection: $selectedTab) {
            Tab("Home", value: "home") {
                Text("Home Content")
            }
            
            Tab("Profile", value: "profile") {
                Text("Profile Content")
            }
            
            Tab("Settings", value: "settings") {
                Text("Settings Content")
            }
        }
    }
}

不需要选择功能的标签视图 (iOS 18+)

swift 复制代码
@available(iOS 18.0, macOS 15.0, *)
struct StaticTabView: View {
    var body: some View {
        TabView {
            Tab("Home") {
                Text("Home Content")
            }
            
            Tab("About") {
                Text("About Content")
            }
        }
    }
}

tabItem

另外tabItem的作用是布局一个视图作为标签页元素,所有可以向其中加入视图,包括允许你指定每个标签页在标签栏中显示的图标和标题。

tabItem 闭包中,你通常会提供一个 Label、Image 或 Text 视图,或者它们的组合。

主要特性

  • 必须与 TabView 一起使用:tabItem 只能应用于 TabView 的子视图
  • 需要配合 tag 修饰符使用:如果你需要跟踪当前选中的标签
  • 自动适应外观:系统会根据当前环境自动调整标签项的外观

高级用法,动态改变标签项

swift 复制代码
.tabItem {
    Label(showDetails ? "Detailed" : "Simple", 
          systemImage: showDetails ? "info.circle.fill" : "info.circle")
}

总结

  • TabView 是 SwiftUI 中创建标签页界面的主要组件
  • 旧版 API 使用 tabItemtag 修饰符定义标签
  • iOS 18+ 引入了新的 Tab 视图和 @TabContentBuilder,提供了更简洁的语法
  • 根据是否需要跟踪当前选中的标签,可以选择不同的初始化方法
  • 当选择值是 Int 类型时,可以使用简化版的初始化方法

新的 iOS 18 API 提供了更直观的方式来定义标签视图,但如果你需要支持旧版系统,仍然需要使用传统的 tabItem 方法。

本文使用 「Markdown 在线编辑器 | 公众号内容排版工具」 排版

相关推荐
怕浪猫3 小时前
FDE 如何正确解决现场项目工程性的问题
前端·后端·ai编程
西瓜太郎12343 小时前
外层模型卡片与分组详情成功率不一致,应该怎么排查?
前端·typescript·go·软件工程·react
GreenTea3 小时前
vLLM 与 SGLang KV Cache 底层实现机制深度调研报告
前端·后端·算法
几何心凉6 小时前
没有万能模型:我用 AiiOnly Token Plan,把多款大模型的长处拼进同一个项目
前端
kyriewen6 小时前
Claude Code 的额度今天缩水了17%——官方公告上写的是"永久提高25%"
前端·ai编程·claude
雪芽蓝域zzs7 小时前
vue解构平铺VS对象包裹
前端·javascript·vue.js
风骏时光牛马8 小时前
从零到实战,完整AI学习路线
前端
三十而立洋8 小时前
深入理解 Monorepo:子包安装依赖
前端·前端工程化
IT_陈寒8 小时前
Java Stream处理大集合,我的内存怎么就炸了
前端·人工智能·后端
Captaincc8 小时前
AI 用量桌面端-桌面宠物自定义指南
前端·人工智能