跳到主要内容

与 UIKit 互操作

问题

如何在 SwiftUI 中使用 UIKit 组件?如何在 UIKit 中嵌入 SwiftUI 视图?

答案

UIViewRepresentable — 在 SwiftUI 中使用 UIKit View

struct MapView: UIViewRepresentable {
let coordinate: CLLocationCoordinate2D

// 创建 UIKit 视图
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
return mapView
}

// 状态变化时更新 UIKit 视图
func updateUIView(_ mapView: MKMapView, context: Context) {
let region = MKCoordinateRegion(center: coordinate, span: .init(latitudeDelta: 0.05, longitudeDelta: 0.05))
mapView.setRegion(region, animated: true)
}

// Coordinator 处理代理回调
func makeCoordinator() -> Coordinator {
Coordinator(self)
}

class Coordinator: NSObject, MKMapViewDelegate {
let parent: MapView
init(_ parent: MapView) { self.parent = parent }
}
}

// 使用
struct ContentView: View {
var body: some View {
MapView(coordinate: .init(latitude: 39.9, longitude: 116.4))
}
}

UIViewControllerRepresentable — 在 SwiftUI 中使用 UIKit VC

struct ImagePicker: UIViewControllerRepresentable {
@Binding var selectedImage: UIImage?
@Environment(\.dismiss) var dismiss

func makeUIViewController(context: Context) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}

func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}

func makeCoordinator() -> Coordinator {
Coordinator(self)
}

class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) { self.parent = parent }

func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
parent.selectedImage = info[.originalImage] as? UIImage
parent.dismiss()
}
}
}

UIHostingController — 在 UIKit 中使用 SwiftUI View

// 在 UIKit VC 中嵌入 SwiftUI
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()

let swiftUIView = ContentView()
let hostingController = UIHostingController(rootView: swiftUIView)

addChild(hostingController)
view.addSubview(hostingController.view)
hostingController.view.frame = view.bounds
hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
hostingController.didMove(toParent: self)
}
}

常见面试问题

Q1: Coordinator 的作用是什么?

答案:Coordinator 充当 UIKit 代理到 SwiftUI 的桥梁。因为 SwiftUI View 是 struct(值类型,会频繁重建),不适合作为代理对象。Coordinator 是 class(引用类型),生命周期稳定,可以安全地作为 UIKit 的 delegate/dataSource。

Q2: 渐进式迁移 UIKit → SwiftUI 的策略?

答案

  1. 新页面用 SwiftUI写,通过 UIHostingController 在 UIKit 导航栈中使用
  2. 复杂 UIKit 组件(如 MKMapView)通过 UIViewRepresentable 在 SwiftUI 中继续使用
  3. 逐步替换简单的 UIKit 页面为纯 SwiftUI
  4. 共享 ViewModel(用 ObservableObject@Observable

相关链接