What's new in SwiftUI
SwiftUI & UI Frameworks 进阶 20m

SwiftUI 的新变化

What's new in SwiftUI

2024年6月10日

在 Apple 官方观看视频

一句话判断

SwiftUI 在 iOS 18 终于有了像样的 TabView 重写(sidebarAdaptable)、Mesh Gradient、Control Widgets、和 zoom transition——框架的完成度到了一个新台阶。

这场 Session 讲了什么

这是一场信息密度极高的 Session,覆盖了 SwiftUI 在 iOS 18 / macOS 15 / visionOS 2 的全面更新。内容分为四个板块。

第一,让 App 焕然一新的功能:TabView 有了全新的 type-safe 语法和 .sidebarAdaptable 样式(可以在 tab bar 和 sidebar 之间切换),Mesh Gradient 让你可以创建漂亮的彩色网格渐变,Swift Charts 支持函数绘图,Table 支持动态列数。第二,平台适配改进:macOS 上可以自定义窗口样式(plain style、floating level、default placement)、visionOS 上有了 pushWindow 和自定义 hover effect。第三,基础构建块的改进:SF Symbols 6 带来了 wiggle/breathe/rotate 三种新动画效果和 MagicReplace。第四,沉浸体验工具:新的 Document Launch Scene、Control Widgets(控制中心和锁屏的自定义控件)。

值得深挖的点

TabView 的 sidebarAdaptable 重写

这是 SwiftUI TabView 自发布以来最大的重构。新的 API 用 type-safe 语法替代了之前的字符串标签方式,编译期能捕获更多错误。核心变化是 .sidebarAdaptable 样式——它让同一个 TabView 在不同场景下自动切换形态:内容少时显示为 floating tab bar,内容多时展开为 sidebar。用户可以通过拖拽来自定义 tab 的顺序和可见性,而且这些自定义是完全可编程控制的。

这个设计解决了一个长期痛点:之前的 TabView 和 NavigationSplitView 是两套独立 API,要做”tab bar 可以展开为 sidebar”的效果需要写大量桥接代码。现在一个 TabView + .sidebarAdaptable 就搞定了。它还在 tvOS 上自动适配为 refreshed sidebar、在 macOS 上可以显示为 toolbar segmented control。这是真正的跨平台自适应。

Control Widgets:控制中心和锁屏的自定义控件

SwiftUI 现在可以创建自定义控件(按钮和开关),这些控件可以出现在控制中心、锁屏、甚至 Action Button 上。Control Widget 是一种新的 Widget 类型,基于 App Intents 构建。

它的价值在于把 App 的核心操作暴露到了系统层面——用户不需要打开你的 App 就能触发操作。比如音乐 App 的”开始播放”按钮、智能家居的”开灯”开关、拍照 App 的”快速自拍”按钮。API 很简洁:ControlWidgetButtonControlWidgetToggle,配合 AppIntent 定义操作逻辑。这延续了 Apple 的趋势:让 App 的功能不再局限在 App 内部,而是渗透到系统的各个触点。

代码片段

新的 TabView 语法

场景:使用 type-safe 的 TabView,支持 sidebar 和 tab bar 切换。

TabView {
    Tab("歌曲", systemImage: "music.note") {
        SongListView()
    }
    Tab("播放列表", systemImage: "list.bullet") {
        PlaylistView()
    }
    Tab("搜索", systemImage: "magnifyingglass") {
        SearchView()
    }
    TabSection("更多") {
        Tab("设置", systemImage: "gear") {
            SettingsView()
        }
        Tab("关于", systemImage: "info.circle") {
            AboutView()
        }
    }
}
.tabViewStyle(.sidebarAdaptable)
// 自动在 tab bar 和 sidebar 之间切换
// 用户可以拖拽自定义 tab 顺序

坑:TabSection 中的 tab 在 tab bar 模式下可能被折叠到”更多”菜单中。

Mesh Gradient

场景:创建彩色网格渐变效果。

MeshGradient(
    width: 3,
    height: 3,
    points: [
        .init(0, 0), .init(0.5, 0), .init(1, 0),
        .init(0, 0.5), .init(0.5, 0.5), .init(1, 0.5),
        .init(0, 1), .init(0.5, 1), .init(1, 1)
    ],
    colors: [
        .purple, .pink, .orange,
        .blue, .white, .yellow,
        .indigo, .mint, .green
    ]
)

坑:points 的数量必须等于 width * height,否则会崩溃。渐变效果在低分辨率设备上可能显示不同。

Control Widget 按钮

场景:在控制中心添加一个快速操作按钮。

import WidgetKit
import AppIntents

struct KaraokeStartControl: ControlWidget {
    var body: some ControlWidgetConfiguration {
        ControlWidgetButton(action: StartKaraokeIntent()) {
            Label("开始 K 歌", systemImage: "mic.fill")
        }
    }
}

// 对应的 AppIntent
struct StartKaraokeIntent: AppIntent {
    static var title: LocalizedStringResource = "开始 K 歌"
    
    func perform() async throws -> some IntentResult {
        // 触发 App 内的唱歌功能
        return .result()
    }
}

坑:Control Widget 需要在 Info.plist 中正确配置,否则不会出现在控制中心。每个 Control Widget 的大小有限制,不能放太多内容。

macOS 自定义窗口

场景:创建一个浮动的歌词预览窗口。

Window("歌词预览", id: "lyrics-preview") {
    LyricsPreviewView()
}
.windowStyle(.plain)           // 移除默认窗口装饰
.windowLevel(.floating)        // 浮在最上层
.defaultWindowPlacement { contentSize, context in
    // 放在屏幕顶部
    let screen = context.screens.first!
    return WindowPlacement(
        position: .init(screen.bounds.midX - contentSize.width / 2,
                        screen.bounds.minY),
        size: contentSize
    )
}
.defaultSize(width: 400, height: 60)

坑:.plain 样式移除了标题栏,用户无法通过标题栏拖动窗口——需要自己加 WindowDragGesture

最佳实践

已有项目:优先迁移 TabView 到新语法——type-safe API 能在编译期发现问题。如果你的 App 使用 NavigationSplitView 模拟 sidebar/tab 切换,现在是替换成 .sidebarAdaptable TabView 的好时机。Mesh Gradient 和 SF Symbols 新动画可以作为视觉升级的一部分逐步加入。

新项目:直接使用新的 TabView API,从第一天就支持 sidebarAdaptable。考虑哪些功能适合做成 Control Widget——频繁使用的操作(如开始记录、切换状态、快捷拍照)是首选。macOS 项目充分利用新的窗口定制能力(plain style、floating level、自定义 placement)。visionOS 项目使用 pushWindow 来管理焦点窗口。

还有什么值得关注

  • Swift Charts 新增函数绘图(LinePlot),可以直接画数学函数图像,也可以叠加实际数据。
  • TableColumnForEach 让 Table 支持动态列数,再也不用为不确定列数的表格头疼了。
  • Sheet 的 presentation sizing 统一为 .form(小表单)和 .page(全页),不再需要手动指定 detents
  • visionOS 上的 hover effect 现在可以自定义闭包来控制视觉反馈,同时保护用户隐私(不暴露精确的眼动数据)。
  • onModifierKeysChanged 让任何 view 都能响应修饰键状态变化,对 macOS 和 iPadOS 的键盘增强体验很有用。
WWDC 2024