Highlight
Xcode 15 的
#Preview宏支持 SwiftUI、UIKit、AppKit 和 Widget 预览,配合 Live/Selection/Variants 三种画布模式,开发者可以在不运行 App 的情况下实时迭代 UI,并直接在真机上预览。
核心内容
你写了一个 SwiftUI 视图,想看一下效果。以前要编译、启动、导航到对应页面。现在用 #Preview 宏,代码写在源文件顶层,Xcode 画布秒级刷新。
(01:25)Preview 是一段编译进 App 的代码,可以访问项目中的所有符号和资源。修改 Swift 代码时,Xcode 只做最小增量编译,然后重新运行 preview。
基本用法
最简单的 preview 只需要一行:
#Preview {
MyView()
}
(05:05)如果视图通常放在 List 里,就在 preview 里放 List:
#Preview {
List {
CollageView(layout: .twoByTwoGrid)
}
.environment(CollageLayoutStore.sample)
}
关键点:
#Preview写在源文件顶层,不嵌套在任何类型或函数里- 可以附加环境修饰符、数据依赖,和 App 的 scene 入口一样
- 命名和配置 trait 作为可选参数传入
(05:37)给 preview 加名字和方向:
#Preview("2x2 Grid", traits: .landscapeLeft) {
List {
CollageView(layout: .twoByTwoGrid)
}
.environment(CollageLayoutStore.sample)
}
UIKit 和 AppKit 支持
(05:58)#Preview 同样支持 UIKit:
#Preview {
var controller = SavedCollagesController()
controller.dataSource = CollagesDataStore.sample
controller.layoutMode = .grid
return controller
}
#Preview("Filter View") {
var view = CollageFilterDisplayView()
view.filter = .bloom(amount: 15.0)
view.imageData = ...
return view
}
关键点:
- 返回 UIViewController 或 UIView 都可以
- 用
var创建实例,配置属性,然后 return - trait 参数对 UIKit preview 同样有效
(11:30)设置 UIKit preview 的初始方向:
#Preview("All Filters", traits: .landscapeLeft) {
let viewController = FilterRenderingViewController()
if let image = UIImage(named: "sample-001")?.cgImage {
viewController.imageData = image
}
viewController.filter = Filter(
bloomAmount: 1.0,
vignetteAmount: 1.0,
saturationAmount: 0.5
)
return viewController
}
三种画布模式
(07:17)画布左下角有三种模式:
- Live 模式(默认):可交互,支持动画和异步代码。可以拖动滑块、测试动画。
- Selection 模式:静态快照。点击 UI 元素,Xcode 高亮对应的源码行。双击文本视图可以直接跳转编辑。
- Variants 模式:同时展示不同设备设置的变体,如所有配色方案或所有动态类型大小。
(08:29)Device Settings 面板在画布底部,可以快速切换 dark mode、动态类型大小,不需要修改代码。
Widget 预览
(12:20)Widget preview 需要指定 widget family 和 timeline provider:
#Preview(as: .systemSmall) {
FrameWidget()
} timelineProvider: {
RandomCollageProvider()
}
关键点:
as:参数指定 widget 尺寸(.systemSmall、.systemMedium 等)timelineProvider:提供时间线数据- Xcode 会快照每个时间线条目,可以用箭头键浏览
- 点击播放按钮可以循环播放时间线过渡动画
(13:28)如果想测试特定的过渡场景,用 timeline 代替 timelineProvider:
#Preview(as: .systemSmall) {
FrameWidget()
} timeline: {
TimelineEntry(date: .now, collage: collageA)
TimelineEntry(date: .now.addingTimeInterval(3600), collage: collageB)
}
Pin 和循环播放
(13:58)点击画布左上角的 pin 按钮,可以在切换到其他文件时保持 preview 活跃。配合循环播放按钮,可以持续观察某个动画过渡效果,同时修改代码。
详细内容
在 Library Target 中使用 Preview
(16:15)Preview 需要可执行文件(App 或 Widget)来运行。Xcode 通过以下规则选择:
- 当前编辑的源文件所属 target
- 这些 target 的所有依赖
- 与当前 scheme 的交集
如果没有 App,Xcode 会自动创建 XCPreviewAgent。如果 view 需要特定 Info.plist 键(如相册权限),可以创建一个专门的 preview app target,添加所需权限,然后把 library 嵌入进去。
Development Assets
(20:30)Preview 用的图片等资源可以放在 Development Assets 文件夹中。这些资源在开发时使用,提交 App Store 时自动移除。
设置方式:Project Settings > Build Settings > Development Assets,添加文件夹路径。新建项目时默认已有一个 Preview Content 文件夹。
真机预览
(23:25)画布底部的设备选择器可以选择连接的真机。选择后 Xcode 直接为真机构建和预览,绕过模拟器。
真机预览的优势:
- 访问相机、传感器等硬件
- 使用设备上的真实数据(如照片库)
- 最高保真度的 UI 验证
(25:07)修改代码后,真机上的 preview 即时更新:
.navigationTitle("Add Collage")
加一行代码,真机立刻显示新标题。
核心启发
-
做什么:给项目中每个自定义视图加
#Preview -
为什么值得做:修改视图代码后秒级看到效果,不需要编译运行整个 App。UIKit 项目也能享受这个工作流
-
怎么开始:在视图源文件顶层加
#Preview { YourView() },确保画布模式已开启 -
做什么:用 Variants 模式检查所有动态类型大小下的布局
-
为什么值得做:Accessibility 要求支持最大字体。Variants 模式一次性展示所有尺寸,快速发现截断或重叠问题
-
怎么开始:点击画布 Variants 按钮,选择 Dynamic Type,检查每个变体的布局
-
做什么:给 Widget 加 timeline preview,测试时间线过渡动画
-
为什么值得做:Widget 的动画只在时间线条目切换时发生,平时很难测试。Preview 可以循环播放这些过渡
-
怎么开始:用
#Preview(as: .systemSmall) { MyWidget() } timelineProvider: { MyProvider() } -
做什么:用真机预览测试依赖设备数据的视图
-
为什么值得做:模拟器没有真实照片、联系人、健康数据。真机 preview 可以直接用这些真实数据验证
-
怎么开始:用设备选择器选连接的真机,确保 Development Assets 里有需要的测试资源
关联 Session
- Prototype with Xcode Playgrounds — 用 Playground 做算法原型,与 Preview 的 UI 迭代互补
- Bring widgets to life — Widget 动画和交互的深入讲解
- What’s new in SwiftUI — SwiftUI 新特性,很多视图可以直接用 Preview 验证
评论
GitHub Issues · utterances