WWDC Quick Look 💓 By SwiftGGTeam
What’s new in SwiftUI

What’s new in SwiftUI

观看原视频

Highlight

TabView 新增 type-safe 语法和 .sidebarAdaptable 样式,一份代码即可在 tab bar 与 sidebar 之间切换,用户还能自由排序和隐藏标签页。


核心内容

iPad 上的 sidebar 应用一直有个尴尬的问题:sidebar 在内容多时好用,但屏幕空间有限时 tab bar 更紧凑。之前开发者只能用 NavigationSplitView 模拟这种切换,代码复杂且行为不一致。iOS 18 给 TabView 加了 .sidebarAdaptable 样式,一行修饰符就能让用户在 tab bar 和 sidebar 之间自由切换,系统还自带标签页排序和隐藏的定制功能。

SwiftUI 这次更新覆盖了四个板块。第一,让 App 焕然一新:TabView 的新语法、Mesh Gradient、Swift Charts 函数绘图、动态 Table 列、Control Widgets。第二,平台适配:macOS 窗口样式定制(plain style、floating level、默认位置)、visionOS 的 pushWindow 和自定义 hover effect、Apple Pencil Pro 的 squeeze 手势。第三,框架基础:自定义容器(subviewOf)、@Entry 宏、文本选择 API、onScrollGeometryChange、Swift 6 兼容性、UIKit/AppKit 动画桥接。第四,沉浸体验:volume baseplate 可见性控制、沉浸空间允许级别、passthrough 视频效果、自定义 TextRenderer。


详细内容

TabView 新语法

TabView 有了 type-safe 的 Tab 构造器,替代了旧的字符串 tabItem。每个 Tab 可以加 customizationID,配合 TabViewCustomization 状态保存用户的排序和隐藏偏好(01:38)。

TabView {
    Tab("Parties", image: "party.popper") {
        PartiesView(parties: Party.all)
    }
    .customizationID("karaoke.tab.parties")

    Tab("Planning", image: "pencil.and.list.clipboard") {
        PlanningView()
    }
    .customizationID("karaoke.tab.planning")
}
.tabViewStyle(.sidebarAdaptable)
.tabViewCustomization($customization)

关键点:

  • Tab("Parties", image:...) 替代 .tabItem { Label(...) },编译期就能发现标签配置错误
  • .sidebarAdaptable 让用户在 tab bar 和 sidebar 之间一键切换
  • $customization 绑定保存了用户的定制状态,下次打开自动恢复

Sheet 统一尺寸 + Zoom 过渡

Sheet 弹窗的尺寸终于有了跨平台统一方案。.presentationSizing(.form) 生成紧凑表单,.page 生成全页(02:28)。导航过渡新增 .zoom 效果,配合 matchedTransitionSource@Namespace 实现元素缩放转场(02:39)。

Control Widgets

新增 ControlWidget 协议,只需几行代码就能创建出现在控制中心或锁屏的按钮或开关,甚至可以绑定到 Action Button(03:18)。

struct StartPartyControl: ControlWidget {
    var body: some ControlWidgetConfiguration {
        StaticControlConfiguration(kind: "com.apple.karaoke_start_party") {
            ControlWidgetButton(action: StartPartyIntent()) {
                Label("Start the Party!", systemImage: "music.mic")
                Text(PartyManager.shared.nextParty.name)
            }
        }
    }
}

关键点:

  • ControlWidgetButton 的 action 接收 AppIntent,和 Shortcuts/App Intents 体系打通
  • 控件可以显示动态内容(如下一场派对名称)

Swift Charts 函数绘图 + 动态 Table 列

LinePlot 可以直接绘制数学函数(03:49)。TableColumnForEach 让列数可以动态生成(04:18)。

Mesh Gradient

通过在网格点之间插值颜色,创建丰富的渐变效果(04:42)。

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

关键点:

  • points 数组定义网格坐标(0~1 范围),可以偏移以制造不对称渐变
  • colors 数组与 points 一一对应,系统自动插值

macOS 窗口定制

Window scene 支持 .windowStyle(.plain) 去掉默认窗口装饰、.windowLevel(.floating) 置顶、defaultWindowPlacement 控制初始位置(07:04)。WindowDragGesture 让无标题栏窗口也能拖动(07:30)。

visionOS:pushWindow + Hover Effect

pushWindow 环境动作可以打开一个窗口同时隐藏来源窗口,适合聚焦式工作流(08:18)。新的闭包式 hoverEffect 可以精确控制 hover 状态下的缩放等视觉反馈(08:47)。

@Entry 宏 + 自定义容器

@Entry 宏一行代码替代整个 EnvironmentKey 协议实现,适用于 EnvironmentValuesFocusValuesTransactionContainerValues13:52)。ForEach(subviewOf:) 可以遍历子视图,构建自定义容器(13:13)。

extension EnvironmentValues {
    @Entry var karaokePartyColor: Color = .purple
}

文本选择 + 搜索焦点 + 文本建议

TextField 新增 selection 绑定,可以读写选区(15:06)。.searchFocused 绑定控制搜索框焦点(15:29)。.textInputSuggestions 给任意文本框添加自动补全(15:41)。

ScrollView 新 API

onScrollGeometryChange 高效响应滚动偏移变化(16:23),onScrollVisibilityChange 检测视图因滚动进入或离开屏幕(16:42),ScrollPosition.scrollTo(edge:) 程序化滚到边缘(16:54)。

UIKit/AppKit 动画桥接

UIView.animate(animation:)NSAnimationContext.animate(animation:) 让 UIKit/AppKit 的属性变更用 SwiftUI 弹簧动画驱动,手势驱动的动画自动保留速度(18:34)。

visionOS 沉浸空间控制

volume 支持 .volumeBaseplateVisibility 控制底板显示(19:59),.onVolumeViewpointChange 响应观察角度变化(20:15)。沉浸空间的 ImmersionStyle.progressive 现在可以限定最小和初始沉浸比例(20:38),.preferredSurroundingsEffect(.colorMultiply(.purple)) 给 passthrough 视频叠加颜色滤镜(21:00)。

自定义 TextRenderer

TextRenderer 协议让你在 Text 视图上叠加自定义绘制,比如模糊发光效果(21:33)。

struct KaraokeRenderer: TextRenderer {
    func draw(layout: Text.Layout, in context: inout GraphicsContext) {
        for line in layout {
            for run in line {
                var glow = context
                glow.addFilter(.blur(radius: 8))
                glow.addFilter(purpleColorFilter)
                glow.draw(run)
                context.draw(run)
            }
        }
    }
}

Text("A Whole View World")
    .textRenderer(KaraokeRenderer())

关键点:

  • 遍历 Text.Layout 的 line 和 run,可以对每个片段单独绘制
  • 先画一层模糊的副本作为发光底,再画原始文字,制造发光效果

核心启发

  1. 给 iPad App 加 sidebar/tab 切换:已有 NavigationSplitView 模拟 sidebar 的项目,迁移到 .sidebarAdaptable TabView 后代码更少、行为和系统一致,用户还能自定义标签页。怎么开始:把 NavigationSplitView 替换为 TabView + Tab + .sidebarAdaptable,给每个 Tab 加 customizationID

  2. 用 Control Widget 把高频操作放到锁屏/控制中心:用户不需要打开 App 就能触发核心功能(开始记录、切换状态、快捷拍照),和 App Intents 打通后还能被 Shortcuts 调用。怎么开始:创建 ControlWidget 实现,定义 ControlWidgetButtonControlWidgetToggle,绑定 AppIntent

  3. onScrollVisibilityChange 做懒加载视频播放:列表中的视频进入视口自动播放、离开视口自动暂停,省电且体验流畅。怎么开始:在 VideoPlayer 上加 .onScrollVisibilityChange(threshold: 0.2) 回调控制 play/pause。

  4. UIView.animate(animation:) 统一 UIKit 和 SwiftUI 动画:混编项目中 UIKit 部分的属性动画现在可以用 SwiftUI 弹簧曲线驱动,手势驱动的动画自动保留惯性速度,不再需要手动计算 timing curve。怎么开始:在 UIView.animate(SwiftUI.Animation.spring(duration: 0.8)) 闭包中修改 UIKit 属性。


关联 Session

评论

GitHub Issues · utterances