iPadOS 18 中 Tab Bar 和 Sidebar 的全面升级
Elevate your tab and sidebar experience in iPadOS
2024年6月10日
一句话判断
iPadOS 18 的 Tab Bar 重新设计为顶部紧凑样式并可与 Sidebar 无缝切换,SwiftUI 和 UIKit 都有新的 API 来描述 App 结构,支持用户自定义 tab 排列——编译 iPadOS 18 SDK 就自动获得新外观。
这场 Session 讲了什么
iPad 上的 Tab Bar 从 iOS 一诞生就存在,但 iPadOS 18 给了它一次大改版。新 Tab Bar 更紧凑,减少不必要的空白,移到屏幕顶部与导航栏共享空间,让内容区域更大。用 iPadOS 18 SDK 编译的现有 App 无需代码改动就会自动获得新外观。
Tab Bar 现在可以展开为 Sidebar。Sidebar 不再需要用 UISplitViewController + Outline View 组合来构建,直接通过 Tab 的层级描述就能自动生成。用户可以在 Sidebar 中看到更多层级的内容(如文件夹、播放列表),收起 Sidebar 后回到 Tab Bar 又能快速切换。两种形态之间有流畅的动画过渡。
用户自定义是另一个重点。Sidebar 中可以拖拽 tab 到 Tab Bar 固定,也可以隐藏不需要的 tab。Tab Bar 分为三个区域:固定区(不可自定义)、可定制区(可拖拽排列)、钉选区(始终在尾部可见,如搜索)。
SwiftUI 引入了新的 Tab 和 TabSection 语法,UIKit 引入了 UITab 和 UITabGroup,都用来统一描述 App 的层级结构。
值得深挖的点
Tab 和 Sidebar 的统一描述模型
之前的痛点是 Tab Bar 和 Sidebar 需要分别构建。Tab Bar 用 UITabBar 或 TabView,Sidebar 用 UISplitViewController 或 NavigationSplitView。两套代码维护成本高,数据源还要同步。
iOS 18 的思路是用一套数据结构同时描述两者。SwiftUI 中 Tab 表示一个顶层导航项,TabSection 表示 Sidebar 中的分组。UIKit 中对应 UITab 和 UITabGroup。你只描述层级关系,系统根据当前展示形态(Tab Bar 或 Sidebar)自动渲染。
这个设计的好处在支持用户自定义时更加明显。自定义的顺序和可见性会自动持久化,不需要开发者自己管理存储。Tab 的 preferredPlacement 属性控制它在 Tab Bar 中的位置偏好(fixed、customizable、pinned、sidebarOnly),系统据此处理自定义逻辑。
Symbol Image 的自动变体选择
新 Tab Bar 有一个细节值得注意:Tab Bar 中使用 filled(实心)图标,Sidebar 中使用 outlined(空心)图标。你只需要提供 outline 变体的 Symbol Image,系统会自动在 Tab Bar 中切换为 filled 变体。这意味着你不需要为两种形态准备两套图标,但前提是你必须使用 SF Symbols 的 outline 变体。
代码片段
SwiftUI 新 Tab 语法 + Sidebar
import SwiftUI
struct ContentView: View {
@State private var selectedTab: String = "browse"
var body: some View {
TabView(selection: $selectedTab) {
Tab("浏览", systemImage: "square.grid.2x2", value: "browse") {
BrowseView()
}
Tab("搜索", systemImage: "magnifyingglass", value: "search") {
SearchView()
}
.pinnedPlacement() // 始终固定在尾部
TabSection("收藏") {
Tab("最近添加", systemImage: "clock", value: "recent") {
RecentView()
}
Tab("最常播放", systemImage: "heart", value: "favorites") {
FavoritesView()
}
}
}
.tabViewStyle(.sidebarAdaptable) // 启用 sidebar 模式
}
}
场景:音乐类 App,Tab Bar 显示主要入口,展开 Sidebar 后能看到”收藏”分组下的更多入口。
坑点:TabSection 在 Tab Bar 中不显示,只在 Sidebar 中以分组形式出现。TabSection 内的 Tab 在 Tab Bar 的可定制区显示,用户可以拖拽到 Tab Bar 固定。
UIKit UITabBarController 新 API
import UIKit
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
// 启用 sidebar 模式
mode = .tabSidebar
// 创建顶层 tab
let browseTab = UITab(title: "浏览", image: UIImage(systemName: "square.grid.2x2")) {
BrowseViewController()
}
let searchTab = UISearchTab() // 内置搜索 tab,自动配置图标和固定位置
// 创建 sidebar 分组
let favoritesGroup = UITabGroup(title: "收藏") { group in
// 分组内的子 tab
}
favoritesGroup.children = [
UITab(title: "最近添加", image: UIImage(systemName: "clock")) {
RecentViewController()
},
UITab(title: "最常播放", image: UIImage(systemName: "heart")) {
FavoritesViewController()
}
]
tabs = [browseTab, searchTab, favoritesGroup]
}
}
场景:UIKit 项目的迁移。UITab 替代了之前通过 viewControllers 数组设置 tab 的方式。
坑点:mode = .tabSidebar 必须显式设置才会启用 sidebar 功能。UITab 的内容使用闭包提供,不再是直接赋值 view controller。
支持拖拽到 Tab
// SwiftUI: 为 Tab 添加 drop destination
Tab("收藏", systemImage: "heart", value: "favorites") {
FavoritesView()
}
.dropDestination(for: Song.self) { songs, _ in
// 处理拖入的歌曲
handleDroppedSongs(songs)
return true
}
场景:用户从列表中拖拽一首歌到 Sidebar 的”收藏”tab 上,直接添加到收藏列表。
最佳实践
- 先编译 iPadOS 18 SDK,观察现有 Tab Bar 的自动升级效果,再做额外调整
- 使用 SF Symbols 的 outline 变体,让系统自动处理 Tab Bar 和 Sidebar 的图标差异
- Tab 数量控制在合理范围,太多选项会让用户难以定位
- 搜索功能用
TabRole.search(SwiftUI)或UISearchTab(UIKit),自动获得固定位置和默认图标 - iPhone 和 iPad 的 Tab 保持一致,不要因为是 iPad 就额外增加 tab
还有什么值得关注
- Sidebar 支持 header/footer 自定义、swipe actions、context menu 和 popover
- Tab Bar 的自定义行为类似 iPadOS 16 的 Toolbar 自定义,习惯一致
sidebarOnlyplacement 可以让某些内容只在 Sidebar 中可见,不出现在 Tab Bar