Xcode UIKit Previews 完整解读

2 阅读4分钟

如果你写过 SwiftUI,一定很熟悉实时预览:改代码画布立刻看到界面效果,不用跑模拟器。 但以前写 UIKit 是非常痛苦的,每次修改控件都要启动模拟器编译运行才能看效果。

Xcode15 + iOS17,苹果正式推出 UIKit Previews。可以直接在 Xcode 画布预览 UIKit 的 View、ViewController,不用启动模拟器 / 真机运行 App,对 UIKit 项目来说是巨大提升。

用法和 SwiftUI 预览几乎一样,用#Preview宏把UIView或者UIViewController包起来即可。


各个使用场景、代码、坑点详解

#Preview 是 Swift 宏;@available(iOS 17, *) 必须加,低于 iOS17 系统没有这个能力。

场景 1:预览普通控件(UILabel / UIButton)

import UIKit

class ViewController: UIViewController {
    // 创建一个红色label
    func makeLabel() -> UILabel {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
        label.backgroundColor = .red
        label.text = "Hello, World!"
        label.textAlignment = .center
        return label
    }
}

// MARK: - UIKit预览代码
@available(iOS 17, *)
#Preview {
    // 直接返回要预览的UIView对象
    ViewController().makeLabel()
}

做了什么:

  1. makeLabel() 构造 UILabel 实例。
  2. #Preview { } 闭包里面返回 UIView,Xcode 右侧画布就渲染出这个控件。
  3. 修改代码,画布实时刷新,不需要启动模拟器。

⚠️注意:ViewController()只是临时实例,不会执行 App 完整启动流程,只是单纯渲染 UI。

场景 2:预览自定义 UIView(最常用!业务自定义组件)

写自定义CustomView,比如自定义按钮、卡片、cell 内部 view,直接预览组件效果。

import UIKit

class CustomView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupUI() {
        backgroundColor = .blue
        
        let label = UILabel()
        label.text = "UIKit Previews! 🚀"
        label.textColor = .white
        label.textAlignment = .center
        label.translatesAutoresizingMaskIntoConstraints = false
        
        addSubview(label)
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: centerXAnchor),
            label.centerYAnchor.constraint(equalTo: centerYAnchor)
        ])
    }
}

// 预览自定义View
@available(iOS 17, *)
#Preview {
    // 指定宽高frame,画布就按这个尺寸渲染组件
    CustomView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
}

✅业务场景 写自定义卡片、输入框、Cell 的 contentView、弹窗组件时,写完立刻看样式,不用把页面 push 出来看。

场景 3:预览整个 UIViewController(整个页面预览)

可以直接预览完整控制器,整个页面渲染在画布。

import UIKit

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemGreen
    }
}

// 预览控制器
@available(iOS 17, *)
#Preview {
    MyViewController()
}

内部会自动读取 vc 的view进行渲染。

进阶扩展(原文没有,实际开发高频用法)

① 多设备预览,同时看手机、平板

@available(iOS 17, *)
#Preview("手机", traits: .init(width: 393, height: 852)) {
    MyViewController()
}

#Preview("平板", traits: .init(width: 1024, height: 768)) {
    MyViewController()
}

② 预览带导航栏的页面(UINavigationController 嵌套)

很多页面依赖导航栏,直接预览 vc 看不到导航栏效果,包一层导航控制器:

@available(iOS 17, *)
#Preview {
    UINavigationController(rootViewController: MyViewController())
}

③ 预览 UITableViewCell

cell 预览,不用跑模拟器进列表:

class DemoCell: UITableViewCell {
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        textLabel?.text = "测试Cell"
    }
    required init?(coder: NSCoder) { fatalError("") }
}

@available(iOS 17, *)
#Preview {
    DemoCell(style: .default, reuseIdentifier: nil)
}

UIKit Previews 核心优点

  1. 不用等待模拟器启动编译,改 UI 立刻看效果;
  2. 自定义 View、Cell、ViewController 快速调样式;
  3. 和 SwiftUI 预览画布是同一个。

⚠️重点坑(官方文档没有,踩坑必看)

  1. 最低版本限制:iOS17 / Xcode15 项目 Deployment Target 如果低于 iOS17,必须加@available(iOS 17, *)包裹#Preview,否则编译报错。

这个预览只是开发期工具,#Preview代码打包 App 的时候会自动剥离,不会打进安装包。

  1. 预览环境 ≠ 真实 App 运行环境
  • SceneDelegate、AppDelegate不会执行;
  • 网络请求、数据库、通知、单例逻辑不会完整走;
  • 适合只看 UI 布局样式;不要依赖业务逻辑执行。

如果你的控件强依赖网络数据、全局状态,预览会显示空白或者报错。可以给预览做模拟假数据。

  1. AutoLayout 约束报错: 自定义 View 如果没给 frame,或者约束缺少,画布会报布局警告,和模拟器报错行为一致。
  2. 不能预览 iOS16 及以下设备,是 Xcode 开发工具能力,不是运行时能力。

什么时候适合用 / 不适合用

✅适合

  • 自定义 UI 组件、卡片、按钮、Cell 布局调样式;
  • 静态页面 UI 调试;
  • 快速查看颜色、圆角、约束效果。

❌不适合

  • 强依赖网络、登录状态、数据库的复杂业务页面;
  • 需要测试点击交互、跳转逻辑(预览不能真正点击交互,只是看静态 UI)。

和 SwiftUI Preview 简单对比

表格

SwiftUI PreviewUIKit Previews(#Preview)
最低版本Xcode11+Xcode15 + iOS17+
返回内容SwiftUI ViewUIView / UIViewController
交互可以简单交互静态预览,不能真正点击
打包行为自动剥离自动剥离