PreviewProvider — 是什么,SwiftUI 协议和 Xcode 配置

作者: IT Sectr 发布日期: 2026-06-27 阅读时间: 10 分钟

PreviewProvider — 是 SwiftUI 协议,它定义了在 Xcode Canvas 中生成预览的入口点。实现该协议使开发者无需启动模拟器即可看到界面,加速了设计阶段的迭代。根据 Apple Developer Documentation(2026),如果项目使用 Canvas,则 PreviewProvider 对所有 SwiftUI View 都是必需的 — 没有它,Canvas 将无法显示用户界面。了解更多信息,请参阅 关于 SwiftUI 的文章

要点

  • PreviewProvider — 用于在 Canvas 中生成 Xcode 预览的 SwiftUI 协议。
  • 一个要求 — 该协议包含一个计算属性 previews: some View。
  • 多个预览 — 通过 Group 可以显示一个 View 的多个状态。
  • 设备配置 — previewDevice、previewLayout 和 displayName 配置显示。
  • UIKit 兼容性 — UIViewRepresentable 和 UIViewControllerRepresentable 也支持 PreviewProvider。

什么是 PreviewProvider?

PreviewProvider — 是定义在 Xcode Canvas 中创建预览内容约定的 SwiftUI 协议。该协议包含一个必需属性:previews,类型为 some View。previews 返回的任何值都会在 Canvas 中显示为交互式预览。PreviewProvider 不需要继承 — 在 extension 中进行静态实现就足够了。

从架构上讲,PreviewProvider 不是 SwiftUI 运行时的一部分 — 它纯粹是一个开发工具。该协议使用 @available(iOS 13.0, *) 属性标记,不会在 release 版本中编译,因为 Xcode 使用条件编译将预览代码从生产版本中排除。这意味着 PreviewProvider 不会影响二进制文件大小和应用程序性能。

previews 协议

previews 属性 — PreviewProvider 的唯一要求。它必须返回任何 View:从简单的 Text 到包含 Group 和 ForEach 的复杂层次结构。Xcode 在 Canvas 中渲染返回的 View,应用系统设置(主题、大小、字体)。

swift
import SwiftUI

struct GreetingView: View {
    let name: String
    
    var body: some View {
        Text("你好,\(name)!")
            .padding()
    }
}

// PreviewProvider — 静态实现
struct GreetingView_Previews: PreviewProvider {
    static var previews: some View {
        GreetingView(name: "World")
    }
}

命名约定:Apple 建议将预览结构命名为 {ViewName}_Previews。这不是编译器的强制要求,但可以提高可读性和项目导航。Xcode 在创建新的 SwiftUI 文件时会自动替换此模板。

PreviewProvider 如何工作:协议和 previews 方法

工作机制 PreviewProvider 基于静态分发:Xcode 仅为 Debug 配置编译包含 PreviewProvider 的 extension,并在构建 Canvas 的过程中调用 previews。每次代码更改时,Xcode 只重新编译已更改的 PreviewProvider,从而确保近乎即时的预览更新。

SwiftUI 不保证预览与模拟器或设备上的最终 UI 完全匹配 — Canvas 使用简化的渲染。带有延迟的动画可能无法正确显示,并且某些 UIKit 组件(MapKit、WebView)在没有额外配置的情况下无法在 Canvas 中渲染。

通过 Group 实现多个预览

Group 允许同时显示一个 View 的多个状态,从而加速不同配置设计时的迭代。Group 内的每个预览独立渲染。

swift
struct ButtonView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ButtonView(title: "Primary", style: .primary)
                .previewDisplayName("Primary")
            
            ButtonView(title: "Disabled", style: .primary)
                .disabled(true)
                .previewDisplayName("Disabled")
            
            ButtonView(title: "Secondary", style: .secondary)
                .previewDisplayName("Secondary")
        }
    }
}

previewDisplayName 为 Canvas 中的每个预览添加标签,这在比较多个状态时特别有用。Group 中的最大预览数没有限制,但超过 6–8 个会减慢 Canvas 的速度。

在 Xcode 中配置预览

Xcode 提供 多个修饰符用于配置预览显示。主要的有:previewDevice — 模拟特定设备(iPhone 16 Pro、iPad Air、Apple Watch Ultra),previewLayout — 设置大小(device、fixed、sizeThatFits)。这些修饰符的组合提供了对预览环境的完全控制。

previewDevice 接受包含设备名称的字符串,例如 “iPhone 16 Pro” 或 “iPad Pro 13-inch (M4)”。可用设备列表取决于 Xcode 中安装的模拟器。如果未找到设备,Canvas 会在默认设备上显示预览而不报错。

修饰符描述示例
previewDevice设备模拟.previewDevice(“iPhone 16 Pro”)
previewLayout大小模式.previewLayout(.sizeThatFits)
previewDisplayName预览标签.previewDisplayName(“Dark Mode”)
preferredColorScheme设计主题.preferredColorScheme(.dark)
dynamicTypeSize字体大小.dynamicTypeSize(.xxxLarge)

不同设备的预览

常见做法 — 同时在多个设备上显示一个 View 以检查响应性。为此,使用带有设备名称数组的 ForEach。

swift
struct AdaptiveView_Previews: PreviewProvider {
    static var previews: some View {
        ForEach(["iPhone SE (3rd generation)", "iPhone 16 Pro Max", "iPad Pro 13-inch (M4)"], id: \.self) { device in
            AdaptiveView()
                .previewDevice(.previewDevice(device))
                .previewDisplayName(device)
        }
    }
}

PreviewProvider 示例

实际示例 展示了 PreviewProvider 的不同使用场景:从简单预览到具有实时数据和 UIKit 兼容性的复杂配置。

使用模拟数据的预览

模拟数据 — 当 View 接受模型时预览的标准模式。用测试数据代替真实 API,可以在不启动应用程序的情况下直观检查 UI 状态。

swift
struct UserProfileView: View {
    let user: User
    
    var body: some View {
        VStack {
            AsyncImage(url: user.avatarURL)
                .clipShape(Circle())
            Text(user.name)
                .font(.title)
            Text(user.bio)
                .font(.body)
                .foregroundColor(.secondary)
        }
    }
}

struct UserProfileView_Previews: PreviewProvider {
    static var previews: some View {
        UserProfileView(user: .mock)
            .previewDisplayName("Profile")
        
        UserProfileView(user: .mockLongName)
            .previewDisplayName("Long Name")
    }
}

通过 UIViewRepresentable 实现 UIKit 预览

UIKit 兼容性 — PreviewProvider 也适用于包装在 UIViewRepresentable 中的 UIKit 组件。这允许在 SwiftUI Canvas 中预览现有的 UIKit 视图,而无需迁移整个项目。

swift
struct MapViewRepresentable: UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        MKMapView()
    }
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        // 配置地图
    }
}

struct MapView_Previews: PreviewProvider {
    static var previews: some View {
        MapViewRepresentable()
    }
}

PreviewProvider 和 SwiftUI Canvas

Canvas — 是 Xcode 的可视化编辑器,实时渲染 PreviewProvider 的结果。没有 PreviewProvider 的实现,Canvas 将保持空白。Canvas 和 PreviewProvider 协同工作:PreviewProvider 确定显示什么,Canvas — 确定在哪里以及如何显示。

重要的是要理解:Canvas — 是预览的执行环境,而不是 PreviewProvider 的替代品。即使开发者没有打开 Canvas,PreviewProvider 也可以用于通过将鼠标悬停在 Canvas 图标上出现的预览来快速检查代码。根据 WWDC 2024,Apple 建议为每个 View 编写 PreviewProvider 作为开发标准,类似于编写单元测试。

组件角色必需性
PreviewProvider确定预览内容Canvas 必需
Canvas在编辑器中渲染预览可选(可以使用 .preview)
SwiftUI ViewUI 组件必需

建议:为项目中每个公共 View 编写 PreviewProvider。这可以加速新开发人员的入职,简化代码审查,并允许在不构建整个项目的情况下快速检查视觉更改。

PreviewProvider 的常见问题

问题 1:预览不更新。如果 Canvas 未反映代码更改,原因通常是 DerivedData 缓存。通过 Product → Clean Build Folder(⇧⌘K)或手动删除 ~/Library/Developer/Xcode/DerivedData 文件夹来清理 DerivedData。清理后,Canvas 从头开始重建预览。

问题 2:PreviewProvider 看不到 @StateObject。PreviewProvider 创建 View 的静态实例,因此需要注入的依赖项(ViewModel、服务)必须通过初始化器或带有默认值的 @StateObject 传递。在预览中使用模拟对象而不是真实服务。

问题 3:动画在 Canvas 中不起作用。Canvas 不支持所有 SwiftUI 动画 — 特别是那些依赖于时间的动画(带延迟的 withAnimation、.spring)。要检查动画,请在模拟器上运行应用程序。Canvas 适用于静态布局检查。

修复具有依赖项的 PreviewProvider

依赖注入 — 使 PreviewProvider 与复杂 ViewModel 一起工作的最佳方法。创建一个带有测试数据的单独 ViewModel 实例,并将其传递给 View 的初始化器。

swift
struct DashboardView: View {
    @StateObject var viewModel: DashboardViewModel
    
    var body: some View {
        List(viewModel.items) { item in
            Text(item.title)
        }
    }
}

struct DashboardView_Previews: PreviewProvider {
    static var previews: some View {
        DashboardView(viewModel: DashboardViewModel.mock)
    }
}

模拟扩展:为 ViewModel 创建一个提供静态 .mock 实例的 extension。这使测试数据与 ViewModel 保持在一起,并使 PreviewProvider 易于阅读。

常见问题解答

是否必须为每个 View 编写 PreviewProvider?

从技术上讲不是 — 应用程序在没有 PreviewProvider 的情况下也能编译。但在实践中,Apple 和 SwiftUI 社区建议为每个公共 View 编写预览。PreviewProvider 可以加速开发,允许在不同设备上快速检查布局,并作为团队的视觉文档。

为什么 PreviewProvider 有时会显示编译错误?

PreviewProvider 仅将代码添加到 Debug 版本,因此如果预览中使用了 release 配置中不可用的类型,则可能会出现编译错误。当使用 @available 与不支持 Canvas 的平台一起使用,或超出预览的复杂性限制时,也会出现错误。

如何将数据从 API 传递到 PreviewProvider?

直接传递 — 不可能,PreviewProvider 在隔离环境中运行。使用模拟数据:创建带有 .mock 实例的静态 model extension。对于带有 @StateObject 的 View,通过初始化器传递带有测试数据的 ViewModel。这可以在没有网络请求的情况下模拟真实数据。

PreviewProvider 会影响最终的 IPA 大小吗?

不会,PreviewProvider 不影响 release 二进制文件的大小。Xcode 使用条件编译(#if DEBUG / #if !RELEASE)将预览代码从 release 版本中排除。PreviewProvider 代码仅存在于 Debug 配置中,不会进入 App Store 版本。

可以在 Xcode 中调试 PreviewProvider 吗?

可以,Xcode 支持预览调试。在 previews 内部或 View 代码本身中设置断点,然后选择 Product → Preview → Debug Preview。之后,断点将在 Canvas 渲染时触发。这对于分析仅在预览中可见的布局问题非常有用。

总结

  • PreviewProvider — 用于在 Xcode Canvas 中创建预览的 SwiftUI 协议,具有一个 previews 属性。
  • 多个预览 — 带有 ForEach 的 Group 允许在不同设备上显示 View 的多个状态。
  • 修饰符 — previewDevice、previewLayout、preferredColorScheme 和 dynamicTypeSize 配置显示。
  • 隔离性 — PreviewProvider 仅在 Debug 配置中工作,不影响最终的 IPA 大小。
  • 模拟数据 — 对于具有复杂模型的预览,使用静态 .mock 实例。
  • UIKit 支持 — 通过 UIViewRepresentable,PreviewProvider 也适用于 UIKit 组件。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读