先理解问题是什么
现实情况是:SwiftUI 原生组件不够用。很多组件SwiftUI 自己没有直接提供,但 UIKit 里有。
那怎么办?苹果提供了一个"桥接协议":UIViewRepresentable
UIViewRepresentable 是什么
它是一个协议(Protocol) ,作用是:
把一个 UIKit 的
UIView,包装成 SwiftUI 能认识的View
你可以把它理解成一个翻译官 ,SwiftUI 和 UIKit 说的不是同一种语言,UIViewRepresentable 负责在中间翻译。
swift
SwiftUI 世界 翻译官 UIKit 世界
───────────── ────────────────────── ──────────────────
some View ←→ UIViewRepresentable ←→ UIView(任意)
它要求你实现两个方法
swift
protocol UIViewRepresentable {
// 方法一:创建 UIKit 视图(只调用一次)
func makeUIView(context: Context) -> 某种UIView
// 方法二:更新 UIKit 视图(状态变化时调用)
func updateUIView(_ uiView: 某种UIView, context: Context)
}
就这两个,不多(有没有想到什么,OC的NSProxy 是不是也是实现两个方法,虽然八杆子打不着,但是突然想到了)。
makeUIView→ 负责初始化 ,相当于viewDidLoad,只跑一次updateUIView→ 负责同步状态,SwiftUI 的数据变了,你要在这里手动更新 UIKit 视图
我写了一个BlurView,早期SwiftUI background不支持毛玻璃效果
swift
struct BlurView: UIViewRepresentable {
let style: UIBlurEffect.Style // ← 从 SwiftUI 传进来的参数
// 第一步:创建真实的 UIKit 视图
func makeUIView(context: Context) -> some UIView {
let view = UIView(frame: .zero)
view.backgroundColor = .clear
// 这才是核心:UIKit 的毛玻璃视图
let blurEffect = UIBlurEffect(style: style)
let blurView = UIVisualEffectView(effect: blurEffect)
// 用 AutoLayout 让它撑满父视图
blurView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(blurView)
NSLayoutConstraint.activate([
blurView.heightAnchor.constraint(equalTo: view.heightAnchor),
blurView.widthAnchor.constraint(equalTo: view.widthAnchor)
])
return view // ← 把这个 UIKit 视图交给 SwiftUI 管理
}
// 第二步:状态更新时同步(这里暂时不需要做任何事)
func updateUIView(_ uiView: UIViewType, context: Context) {
// 如果 style 会动态变化,就在这里更新
}
}
重点理解 :makeUIView 返回的那个 UIView,之后就由 SwiftUI 的布局系统接管了。你不需要手动设置 frame,SwiftUI 会帮你处理尺寸。
然后 View Extension 做了什么
swift
extension View {
func blurBackground(style: UIBlurEffect.Style) -> some View {
ZStack {
BlurView(style: style) // ← UIKit 毛玻璃,铺在底层
self // ← 原来的 SwiftUI 视图,叠在上层
}
//两个方法都行
//self.background(BlurView(style: style))
}
}
BlurView 在这里和任何 SwiftUI 原生 View 完全没有区别,可以直接放进 ZStack。这就是 UIViewRepresentable 的意义:让 UIKit 视图假装自己是 SwiftUI 视图。
整体调用链是这样的
scss
.blurBackground(style: .systemMaterial)
↓
ZStack 叠加
┌────────────┐
│ BlurView │ ← UIViewRepresentable 在这里翻译
│ (UIKit) │ makeUIView() 被 SwiftUI 自动调用
└────────────┘
↑
self(原 SwiftUI 视图)叠在上面
什么时候用 UIViewRepresentable(有些SwfitUI 现在自己已经有了)
| 场景 | 推荐方案 |
|---|---|
| 毛玻璃、特效 | UIVisualEffectView → UIViewRepresentable |
| 地图 | MKMapView → 或直接用 SwiftUI 的 Map |
| 网页 | WKWebView → UIViewRepresentable |
| 富文本编辑 | UITextView → UIViewRepresentable |
| 相机预览 | AVCaptureVideoPreviewLayer → UIViewRepresentable |
| SwiftUI 能搞定的 | 直接用 SwiftUI,别绕弯子 |
还有一个兄弟协议:UIViewControllerRepresentable
如果你要包装的不是 UIView,而是整个 UIViewController(比如系统的图片选择器、分享弹窗),用这个:
swift
struct ImagePickerView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIImagePickerController {
return UIImagePickerController()
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {
// 同上,同步状态用
}
}
逻辑完全一样,只是把 UIView 换成了 UIViewController。
总结
UIViewRepresentable 本质上就是:
实现两个方法(创建 、更新),让 SwiftUI 知道怎么驾驭一个 UIKit 视图
它解决的核心问题是:SwiftUI 和 UIKit 生命周期不同,这个协议负责在两套系统之间搭桥。
BlurView 是一个非常标准的使用案例------SwiftUI 没有,UIKit 有,包一下,用上。