Canvas — Xcode 的交互式预览编辑器,无需启动模拟器即可实时显示 SwiftUI View。Canvas 在每次代码更改时自动更新,并支持手势、导航和深色模式。根据 Apple Developer Documentation(2026),Canvas 使用独立的 PreviewProviderExtension 渲染进程,允许您编辑代码并立即查看结果,而无需重新编译整个项目。有关 SwiftUI 的更多信息,请阅读 SwiftUI 材料。
要点
Canvas — Xcode 内置的预览编辑器,首次随 Xcode 11 和 SwiftUI 一起推出。它位于编辑器的右侧面板中,位于代码旁边,显示当前 SwiftUI View 的实时预览。Canvas 实时工作:代码中的每次更改都会立即反映在预览中,无需手动重新编译。
从架构上讲,Canvas 是一个独立的进程(Preview Provider Extension),Xcode 在打开 Canvas 时启动该进程。该进程加载编译后的 PreviewProvider,通过 Metal 渲染结果,并将其显示在编辑器面板中。如果未实现 PreviewProvider,Canvas 会显示消息“Preview paused — No preview provider found”。
Canvas 界面包括一个工具栏,用于选择设备、方向、配色方案和缩放。Live Preview、Selectable 和 Embed In Diagram 按钮切换交互模式。Canvas 支持拆分视图:可以在同一工作区中为不同文件打开多个 Canvas。
| Canvas 元素 | 用途 |
|---|---|
| Device selector | 选择预览设备(iPhone、iPad、Apple Watch) |
| Orientation toggle | 切换纵向/横向(iOS、iPadOS) |
| Color scheme | 浅色/深色模式 |
| Dynamic Type slider | 用于无障碍检查的字体缩放 |
| Live Preview | 支持手势的交互模式 |
| Selectable mode | 检查界面元素 |
Live Preview — Canvas 的关键功能,使预览具有交互性。在此模式下,Canvas 在单独的进程中渲染 View,并将手势(点击、滑动、滚动)传回 SwiftUI 运行时。用户无需启动模拟器即可按下按钮、填写文本字段和测试导航。
SwiftUI 通过与实际设备相同的事件系统处理 Canvas 中的手势。性能差异:Canvas 通过 Metal 使用软件渲染,而模拟器使用主机图形。这意味着 Canvas 中的复杂动画可能运行较慢或在视觉上有所不同。
Canvas 更新分三个阶段进行。首先,Xcode 检测文件更改,并仅增量编译更改后的 PreviewProvider。然后,新的二进制模块加载到 PreviewProviderExtension 进程中。最后,SwiftUI 重新创建 View 并通过 Metal 渲染。整个周期需要 0.5–2 秒,具体取决于 View 的复杂程度。
struct TappableButton: View {
@State private var count = 0
var body: some View {
Button("被点击了 \(count) 次") {
count += 1
}
.buttonStyle(.borderedProminent)
}
}
struct TappableButton_Previews: PreviewProvider {
static var previews: some View {
TappableButton()
}
}
交互性:启动 Live Preview 后,Canvas 中的按钮像真实按钮一样工作 — 每次按下计数器都会增加,按下动画也会显示。这允许在没有模拟器的情况下测试按钮逻辑。
基本设置可以通过 Editor → Canvas 菜单或 Canvas 本身的工具栏按钮访问。主要选项包括设备选择、方向、深色模式和 Dynamic Type 缩放。对于永久设置,请在代码中使用 PreviewProvider 修饰符。
高级设置包括:Auto Activate Preview — 打开 SwiftUI 文件时自动激活 Canvas;Live Preview — 手势模式;Draw Live Edges — 显示视图边界;Show Preview Sizes — 预览区域的大小。Xcode 将这些设置可移植地保存在 workspace/项目文件中。
编程配置提供了对 Canvas 更精确的控制。应用于 previews 的修饰符会覆盖工具栏设置并保存在代码中 — 所有团队成员都可以通过 git 看到它们。
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
SettingsView()
.previewDevice("iPhone 16 Pro")
.previewLayout(.device)
.preferredColorScheme(.dark)
.dynamicTypeSize(.xxxLarge)
.previewDisplayName("Dark + XL Text")
}
}
previewLayout 与 .device 显示设备的完整屏幕,而 .sizeThatFits 显示大小适应内容的紧凑预览。对于小部件和小型组件,请使用 .sizeThatFits — 这可以节省编辑器空间。
示例 1:检查适应性。使用 ForEach 搭配多个设备和配色方案,以确保界面在所有屏幕上看起来都一样好。Canvas 同时更新所有预览,让您在启动模拟器之前就可以注意到布局问题。
示例 2:带数据的预览。对于显示动态内容的 View(列表、个人资料、卡片),请在 previews 中创建多个具有不同数据的实例。这比在模拟器中切换屏幕和输入数据更快。
预览组通过 Group 或 ForEach 允许在一个面板上显示组件的所有状态。对于列表来说,这尤其方便:空列表、加载、错误和填充的列表同时可见。
struct LoadingStateView: View {
let state: LoadingState
var body: some View {
switch state {
case .loading:
ProgressView()
case .loaded(let items):
List(items, id: \.self) { Text($0) }
case .error(let message):
Text(message).foregroundColor(.red)
}
}
}
struct LoadingStateView_Previews: PreviewProvider {
static var previews: some View {
Group {
LoadingStateView(state: .loading)
.previewDisplayName("Loading")
LoadingStateView(state: .loaded(["Item 1", "Item 2"]))
.previewDisplayName("Loaded")
LoadingStateView(state: .error("Failed to load"))
.previewDisplayName("Error")
}
}
}
Canvas 和 Simulator 相互补充,并非替代。Canvas 非常适合界面设计时的快速迭代:编辑代码并立即获得反馈。Simulator 对于最终检查是必要的:实际性能、自定义手势、系统警报以及与硬件功能(相机、传感器)的集成。
根据 WWDC 2024,Apple 将 Canvas 定位为早期阶段开发人员的工具,而 Simulator 用于集成测试阶段。建议 UI 开发时间的 60% 在 Canvas 中,40% 在模拟器或设备上检查。
| 特征 | Canvas | Simulator |
|---|---|---|
| 更新速度 | 0.5–2 秒(增量) | 10–60 秒(完全构建) |
| 手势 | 基本(点击、滚动) | 全部(捏合、旋转、3D Touch) |
| 相机/陀螺仪 | 不支持 | 可模拟 |
| 动画 | 有限 | 完全 |
| 推送通知 | 不支持 | 支持 |
| 网络 | 通过 Xcode 进程 | 完整网络栈 |
建议:在 Canvas 中设计,在模拟器上测试。使用 Live Preview 测试按钮和导航的手势逻辑,但动画、网络请求和硬件功能的最终检查应在模拟器或真实设备上进行。
技巧 1:使用 Selectable 模式。在 Selectable 模式(光标图标)下,您可以单击预览中的任何元素,并在检查器中查看其层次结构、修饰符和框架。这对于调试布局很有用:您无需打印即可立即看到元素的边距、偏移和大小。
技巧 2:Embed In Diagram。Canvas 可以对元素进行分组:选择两个或多个 View,单击 Embed In Diagram — Canvas 将创建 VStack/HStack/ZStack 并自动重写代码。这可以加速创建复杂的层次结构,而无需手动输入括号。
技巧 3:清除 Canvas 预览缓存。如果 Canvas 停止更新,请清除 Product → Preview Cache。Xcode 将删除缓存的 PreviewProvider 二进制文件并从头开始重建它们。这可以解决 90% 的 Canvas 卡住问题。
Canvas 缓慢通常是由于预览过多造成的。对于复杂的 View,只使用一个预览,而不是 6–8 个。为没有手势的 View 关闭 Live Preview — 静态模式渲染更快。确保 PreviewProvider 使用模拟数据,而不是真实的网络请求。
// 快速调试:最小预览
struct ComplexView_Previews: PreviewProvider {
static var previews: some View {
ComplexView()
.previewLayout(.sizeThatFits) // 紧凑模式
}
}
previewLayout(.sizeThatFits) — 最快的 Canvas 模式,因为仅渲染 View 的内容,没有设备框架。用于日常工作,仅在最终检查时启用 .device。
常见问题
最常见的原因是当前 View 缺少 PreviewProvider。Canvas 需要在 previews 属性中实现 PreviewProvider 协议并返回 View。其他原因:代码中的编译错误、DerivedData 问题或 PreviewProviderExtension 进程未启动。
是的,Xcode 通过 Product → Preview → Debug Preview 支持预览调试。激活后,View 代码中的断点将在 Canvas 渲染时触发。这允许您分析变量的运行时值并检查显示逻辑。
Canvas 通过 UIViewRepresentable 和 UIViewControllerRepresentable 支持 UIKit 组件。但是,某些组件不会渲染:MapKit、WebView、通过 AVPlayer 的视频、自定义 Metal/GLKit 视图。Canvas 不模拟硬件功能,因此相机和传感器不可用。
减少 Group 中的预览数量(最多 3-4),使用 previewLayout(.sizeThatFits) 而不是 .device,为没有手势的 View 关闭 Live Preview。清除 Product → Preview Cache。确保 PreviewProvider 不进行网络请求 — 使用模拟数据。
Canvas 不会影响最终 IPA 的大小 — PreviewProvider 代码仅在 Debug 配置中编译。在开发过程中,Canvas 会向 DerivedData 添加 100–200 MB 的缓存,由 Xcode 自动管理。定期清理 DerivedData 可以释放空间。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。