Elevate your tab and sidebar experience in iPadOS
System Frameworks 进阶 20m

iPadOS 18 中 Tab Bar 和 Sidebar 的全面升级

Elevate your tab and sidebar experience in iPadOS

2024年6月10日

在 Apple 官方观看视频

一句话判断

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 引入了新的 TabTabSection 语法,UIKit 引入了 UITabUITabGroup,都用来统一描述 App 的层级结构。

值得深挖的点

Tab 和 Sidebar 的统一描述模型

之前的痛点是 Tab Bar 和 Sidebar 需要分别构建。Tab Bar 用 UITabBar 或 TabView,Sidebar 用 UISplitViewController 或 NavigationSplitView。两套代码维护成本高,数据源还要同步。

iOS 18 的思路是用一套数据结构同时描述两者。SwiftUI 中 Tab 表示一个顶层导航项,TabSection 表示 Sidebar 中的分组。UIKit 中对应 UITabUITabGroup。你只描述层级关系,系统根据当前展示形态(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 自定义,习惯一致
  • sidebarOnly placement 可以让某些内容只在 Sidebar 中可见,不出现在 Tab Bar
WWDC 2024