SwiftUI 和 UIKit 是两个强大的框架,使您能够为 iOS 应用程序创建美观且响应灵敏的用户界面。 SwiftUI 是一个现代的声明式框架,可让您使用简单简洁的代码构建 UI。 UIKit 是一个经典的命令式框架,可让您完全控制和灵活地控制 UI 的各个方面。
虽然 SwiftUI 是 iOS 开发的未来,但 UIKit 仍然受到 Apple 和开发者社区的广泛使用和支持。您可能希望在项目中同时使用这两个框架的原因有很多
-
SwiftUI 不兼容的现有 UIKit 组件和库
-
逐步增量地将旧版 UIKit 项目迁移到 SwiftUI
-
试验 SwiftUI 的特性和功能,而无需从头开始重写整个应用程序
-
创建结合了两全其美的混合界面
然而,将 SwiftUI 和 UIKit 结合起来并不总是简单或容易的。您需要了解并克服一些挑战和陷阱。例如:
-
了解如何 SwiftUI 和 UIKit 不同范例和架构之间的差距
-
了解如何在 SwiftUI 视图中包装 UIKit 视图和视图控制器,反之亦然
-
了解如何在 SwiftUI 和 UIKit 视图之间双向传递数据和通信
-
跨两个框架管理 UI 的生命周期和状态
我将指导您了解 SwiftUI 和 UIKit 互操作性的基础知识和高级技术。我将向您展示如何使用 UIViewRepresentable
和 UIViewControllerRepresentable
协议将 UIKit 组件与 SwiftUI 集成。我还将向您展示如何使用 Coordinator
模式来处理复杂的场景,例如导航、委托、回调等。最后,我将向您展示如何使用 UIHostingController
和 SwiftUIHostingView
类将 SwiftUI 视图嵌入 UIKit 视图控制器和视图中
互操作性的重要性
SwiftUI 和 UIKit 是 iOS 应用程序开发中使用的两个流行框架。 SwiftUI 是 Apple 最新的 UI 框架,专注于声明式方法,使创建用户界面变得更容易、更高效。另一方面,旧框架 UIKit 是基于命令式范例构建的,需要更多的手动工作和样板代码。
尽管 SwiftUI 兴起,UIKit 仍然是许多现有应用程序和库的重要组成部分。因此,了解如何结合 SwiftUI 和 UIKit 对于想要利用这两个框架的优势同时保持与现有代码库的兼容性的开发人员至关重要
SwiftUI 和 UIKit 概述
SwiftUI 是 Apple 于 2019 年推出的基于 Swift 的现代 UI 框架。它使用声明性语法简化了创建用户界面的过程,使开发人员能够更简洁地表达 UI 组件及其关系。另一方面,UIKit 是一个基于 Objective-C 的框架,自该平台诞生以来一直是 iOS 开发人员的首选。它提供了一组丰富的 UI 组件和工具,但依赖于可能更加复杂和冗长的命令式编程风格。
SwiftUI 和 UIKit 互操作性入门
先决条件 要遵循本指南
-
Xcode (version 11 or later)
-
对 Swift 和 iOS 应用程序开发有基本了解
创建新项目 要创建新项目
-
启动 Xcode 并选择新的 Xcode 项目
-
选择“单视图应用程序”模板,然后单击“下一步
-
输入项目名称,选择“Swift”作为语言,然后选择“SwiftUI”作为用户界面。点击下一步。”
-
为您的项目选择一个位置,然后单击“创建”。
将 UIKit 集成到 SwiftUI 中
我们将探索将 UIKit 组件集成到 SwiftUI 中的两种方法:UIViewRepresentable 和 UIViewControllerRepresentable。
使用 UIViewRepresentable
UIViewRepresentable 是一个协议,允许您创建包装 UIKit 视图的 SwiftUI 视图。要使用 UIViewRepresentable,请按照下列步骤操作:
-
创建一个新的 Swift 文件并导入 SwiftUI 和 UIKit
-
定义一个符合 UIViewRepresentable 协议的结构体。
-
实现所需的方法:makeUIView(context:) 和 updateUIView (_:context:)
-
下面是在 SwiftUI 视图中包装 UIKit UILabel 的示例
import SwiftUI import UIKit
struct UILabelWrapper: UIViewRepresentable { var text: String
func makeUIView(context: Context) -> UILabel { let label = UILabel() label.textAlignment = .center return label } func updateUIView(_ uiView: UILabel, context: Context) { uiView.text = text }
}
现在可以在 SwiftUI 视图中使用 UILabelWrapper
import SwiftUI
struct ContentView: View {
var body: some View {
VStack {
Text("SwiftUI Text")
UILabelWrapper(text: "UIKit UILabel")
}
}
}
实现 UIViewControllerRepresentable
UIViewControllerRepresentable 与 UIViewRepresentable 类似,但用于包装 UIKit 视图控制器而不是视图。要使用 UIViewControllerRepresentable,请按照下列步骤操作:
-
创建一个新的 Swift 文件并导入 SwiftUI 和 UIKit
-
定义一个符合 UIViewControllerRepresentable 协议的结构体
-
实现所需的方法:makeUIViewController(context:) 和 updateUIViewController(_:context:)。
-
下面是在 SwiftUI 视图中包装 UIImagePickerController 的示例
import SwiftUI import UIKit
struct ImagePicker: UIViewControllerRepresentable { @Binding var image: UIImage? @Environment(.presentationMode) var presentationMode
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]) { if let image = info[.originalImage] as? UIImage { parent.image = image } parent.presentationMode.wrappedValue.dismiss() } }
}
现在可以在 SwiftUI 视图中使用 ImagePicker,如下所示
import SwiftUI
struct ContentView: View {
@State private var image: UIImage?
@State private var showingImagePicker = false
var body: some View {
VStack {
if let image = image {
Image(uiImage: image)
.resizable()
.scaledToFit()
} else {
Text("Select an image")
}
Button("Pick Image") {
showingImagePicker = true
}
}
.sheet(isPresented: $showingImagePicker) {
ImagePicker(image: $image)
}
}
}
将 SwiftUI 集成到 UIKit 中
我们将演示如何使用 UIHostingController 将 SwiftUI 视图集成到 UIKit 中,并将 SwiftUI 视图直接添加到 UIKit 组件中
使用 UIHostingController
UIHostingController 是一个 UIViewController 子类,可以托管 SwiftUI 视图。要使用 UIHostingController,请按照下列步骤操作:
-
创建一个新的 SwiftUI 视图文件
-
在 UIKit 视图控制器中,导入 SwiftUI
-
使用 SwiftUI 视图作为其根视图实例化 UIHostingController。
-
将托管控制器添加为子视图控制器并 addSubview 其视图
下面是将 SwiftUI 视图添加到 UIKit 视图控制器的示例:
import UIKit
import SwiftUI
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swiftUIView = SampleSwiftUIView()
let hostingController = UIHostingController(rootView: swiftUIView)
addChild(hostingController)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingController.view)
NSLayoutConstraint.activate([
hostingController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
hostingController.didMove(toParent: self)
}
}
在此示例中,SampleSwiftUIView 是您要添加到 UIKit 视图控制器中的 SwiftUI 视图
将 SwiftUI 视图添加到 UIKit
您还可以使用 UIView 的 addSubview() 方法将 SwiftUI 视图直接添加到 UIKit 组件。为此,请按照下列步骤操作:
-
在 UIKit 视图或视图控制器中,导入 SwiftUI。
-
创建一个 UIHostingController 实例,并将 SwiftUI 视图作为其根视图
-
将托管控制器的视图添加为 UIKit 视图的子视图
下面是将 SwiftUI 视图添加到 UIKit UIView 的示例
import UIKit
import SwiftUI
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
let swiftUIView = SampleSwiftUIView()
let hostingController = UIHostingController(rootView: swiftUIView)
hostingController.view.translatesAutoresizingMaskIntoConstraints = false
addSubview(hostingController.view)
NSLayoutConstraint.activate([
hostingController.view.topAnchor.constraint(equalTo: topAnchor),
hostingController.view.leadingAnchor.constraint(equalTo: leadingAnchor),
hostingController.view.trailingAnchor.constraint(equalTo: trailingAnchor),
hostingController.view.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}