Highlight
SwiftUI 在 iOS 15 中新增了
Canvas、TimelineView、Materials 和 foreground styles 等 API,让开发者可以用声明式语法实现高性能自定义图形、粒子动画和沉浸式视觉效果。
核心内容
做图形类 App 时,开发者经常面临一个矛盾:想要全屏沉浸的视觉体验,又担心内容被刘海、Home 指示条或键盘遮挡。
以前的做法是手动计算各种 insets,用 GeometryReader 读取安全区域,再调整布局。代码冗长,不同设备表现还不一致。
WWDC2021 的 SwiftUI 带来了几个关键改进,让这个痛点迎刃而解。
Safe Area 的精细化控制
ignoresSafeArea() 修饰符现在可以指定忽略哪些安全区域。比如背景图想要全屏铺满,但内容仍要避开键盘:
ContentView()
.ignoresSafeArea(.keyboard)
(03:52)
更强大的是 safeAreaInset 修饰符。它允许你在某个边缘添加辅助内容(比如底部工具栏),同时自动让主内容的安全区域内缩,避免被遮挡。
List {
// 主内容
}
.safeAreaInset(edge: .bottom) {
// 底部工具栏
HStack {
Button("Cancel") {}
Spacer()
Button("Save") {}
}
.padding()
.background(.thinMaterial)
}
(09:03)
Materials 和 Vibrancy
iOS 15 引入了 Material 模糊效果,从 ultraThin 到 ultraThick 共 6 个级别。配合 .foregroundStyle(.secondary) 等层级样式,文字会自动获得 Vibrancy 效果——根据背景色动态调整对比度。
Text("Hello")
.foregroundStyle(.secondary)
.background(.ultraThinMaterial)
(06:13)
Canvas 和 TimelineView
对于需要绘制大量图形元素的场景(比如粒子系统、数据可视化),Canvas 提供了直接绘制的能力,性能远超组合 View。
TimelineView 则让动画更新可控——你可以用 .animation schedule 获得类似 CADisplayLink 的逐帧回调。
详细内容
前景样式的新体系
foregroundStyle 取代了旧的 foregroundColor,支持更丰富的语义层级:
VStack {
Text("Primary")
.foregroundStyle(.primary)
Text("Secondary")
.foregroundStyle(.secondary)
Text("Tertiary")
.foregroundStyle(.tertiary)
Text("Quaternary")
.foregroundStyle(.quaternary)
}
.foregroundStyle(.purple)
(07:08)
关键点:
.primary到.quaternary是语义层级,在 Material 上下文中自动获得 Vibrancy 效果- 外层
.foregroundStyle(.purple)会把所有子视图的前景色调统一为紫色 - 也可以传入
LinearGradient等复杂样式
let blueGradient = LinearGradient(
colors: [.blue, .teal],
startPoint: .leading,
endPoint: .trailing
)
VStack {
Text("Primary").foregroundStyle(.primary)
Text("Secondary").foregroundStyle(.secondary)
}
.foregroundStyle(blueGradient)
(07:41)
Canvas 绘制基础
Canvas 是一个专门的绘制视图,适合需要直接控制像素的场景:
Canvas { context, size in
// 解析图片(避免每次绘制都重新解析)
let resolvedImage = context.resolve(Image("sparkle"))
// 获取图片尺寸
let imageSize = resolvedImage.size
// 在中心绘制
let rect = CGRect(
x: size.width / 2 - imageSize.width / 2,
y: size.height / 2 - imageSize.height / 2,
width: imageSize.width,
height: imageSize.height
)
context.draw(resolvedImage, in: rect)
}
(13:12)
关键点:
context.resolve()提前解析图片资源,提升性能- 闭包不是
ViewBuilder,可以用普通 for 循环 - 所有绘制按顺序执行,后绘制的覆盖先绘制的
配合 TimelineView 做动画
TimelineView(.animation) { timeline in
Canvas { context, size in
let time = timeline.date.timeIntervalSinceReferenceDate
let angle = Angle(radians: time.remainder(dividingBy: 3) * 2 * .pi / 3 * 2)
let offsetX = cos(angle.radians) * 50
let resolvedImage = context.resolve(Image("sparkle"))
let imageSize = resolvedImage.size
for i in 0..<particleCount {
var contextCopy = context
contextCopy.opacity = 0.7
contextCopy.blendMode = .screen
let rect = CGRect(
x: size.width / 2 + offsetX - imageSize.width / 2,
y: size.height / 2 + CGFloat(i) * 10 - imageSize.height / 2,
width: imageSize.width,
height: imageSize.height
)
contextCopy.draw(resolvedImage, in: rect)
}
}
}
.gesture(
TapGesture()
.onEnded { _ in
particleCount += 1
}
)
(18:38)
关键点:
TimelineView(.animation)以屏幕刷新率触发更新context是值类型,复制后修改不影响原上下文.blendMode(.screen)让重叠部分变亮,适合发光效果- 可以在 Canvas 外层添加手势,但无法给内部单个元素加手势
无障碍支持
Canvas 是单张位图,内部元素对 Accessibility 不可见。需要手动添加描述:
Canvas { context, size in
// 绘制代码
}
.accessibilityLabel("Particle animation showing \(particleCount) sparkles")
.accessibilityAddTraits(.isImage)
(21:22)
核心启发
1. 为照片/视频编辑 App 添加实时滤镜预览
用 Canvas 绘制滤镜后的图像,配合 TimelineView 实现 60fps 的实时预览。入口 API:Canvas + context.draw() + context.filter。
2. 构建自定义图表和数据可视化
对于需要绘制数百个数据点的场景,Canvas 比组合 Shape 性能更好。可以结合 TimelineView 做动态数据流展示。入口 API:Canvas + context.fill(path, with: .color(...))。
3. 实现游戏化的粒子效果
烟花、雪花、星光等效果用 Canvas + blendMode(.screen) 轻松实现。将粒子状态封装到 ObservableObject,TimelineView 驱动更新。入口 API:TimelineView(.animation) + @StateObject。
4. 设计沉浸式阅读/创作界面
用 ignoresSafeArea(.keyboard) 让背景图全屏延伸,内容区域用 safeAreaInset 放置浮动工具栏,Material 做毛玻璃背景。入口 API:.background(Image(...).ignoresSafeArea()) + .safeAreaInset。
5. 为 macOS App 添加 Vibrancy 侧边栏
SwiftUI 的 Material 在 macOS 上自动适配为原生 Vibrancy 效果。用 .listStyle(.sidebar) 配合 .foregroundStyle(.secondary) 即可获得系统级视觉质感。入口 API:.background(.ultraThinMaterial)。
关联 Session
- Demystify SwiftUI — 深入理解 SwiftUI 的 Identity、Lifetime 和 Dependencies
- Direct and reflect focus in SwiftUI — 学习焦点管理,配合图形交互
- SwiftUI on the Mac: Build the fundamentals — macOS 上的 SwiftUI 图形适配
- Discover concurrency in SwiftUI — 后台数据加载与 UI 更新
评论
GitHub Issues · utterances