在目前階段,SwiftUI 很難獨(dú)立開發(fā)一款功能強(qiáng)大的 App,還是需要與 UIKit 一起合作,借助 UIKit 成熟完善的知識體系,二者相互嵌套形成混合開發(fā)。
UIKit in SwiftUI
| UIKit | SwiftUI |
|---|---|
| UIView | UIViewRepresentable |
| UIViewController | UIViewControllerRepresentable |
UIViewRepresentable
- 要使 UIView 在 SwiftUI 中可用,需要用
UIViewRepresentable對 UIView 進(jìn)行包裝。 -
UIViewRepresentable中主要有兩個(gè)方法需要實(shí)現(xiàn):-
makeUIView:創(chuàng)建View。 -
updateUIView:根據(jù)條件和業(yè)務(wù)邏輯設(shè)置View的狀態(tài)。
-
- 案例一
import SwiftUI
import UIKit
struct ActivityIndicator: UIViewRepresentable {
var isAnimating: Bool
func makeUIView(context: Context) -> UIActivityIndicatorView {
let v = UIActivityIndicatorView()
v.color = .orange
return v
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
if isAnimating {
uiView.startAnimating()
} else {
uiView.stopAnimating()
}
}
}
struct ContentView : View {
var isAnimating: Bool = true
var body: some View {
ActivityIndicator(isAnimating: isAnimating)
}
}
- 案例二
import UIKit
import SwiftUI
import MapKit
struct Map: UIViewRepresentable {
var locationManager:CLLocationManager = CLLocationManager()
func setupManager(){
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.requestAlwaysAuthorization()
}
func makeUIView(context: Context) -> MKMapView {
self.setupManager()
let map = MKMapView(frame: UIScreen.main.bounds)
map.showsUserLocation = true
map.userTrackingMode = .followWithHeading
return map
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
}
struct ContentView : View {
var body: some View {
Map()
}
}
- 案例三
// 定義一個(gè)類負(fù)責(zé)實(shí)現(xiàn)代理
class TextFieldDelegate: NSObject, UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
print("開始編輯")
}
}
// 定義UIViewRepresentable
struct MyTextField: UIViewRepresentable {
var text: String
var placeholder: String
private let delegate = TextFieldDelegate()
func makeUIView(context: UIViewRepresentableContext<MyTextField>) -> UITextField {
let tmpView = UITextField()
tmpView.text = text
tmpView.borderStyle = .roundedRect
tmpView.placeholder = placeholder
tmpView.delegate = delegate
return tmpView
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<MyTextField>) {
}
}
// SwiftUI使用
struct ContentView : View {
var body: some View {
MyTextField(text: "", placeholder: "請輸入內(nèi)容").frame(height: 40).padding()
}
}
- 如果要橋接 UIKit 的數(shù)據(jù)綁定(Delegate,Target/Action),需要使用
Coordinator進(jìn)行協(xié)調(diào)。
import SwiftUI
import UIKit
// 自定義個(gè)SegmentControl控件
struct SegmentControl: UIViewRepresentable {
@Binding var selectedSegmentIndex: Int
// 下面兩個(gè)方法都是和 UIKit 相關(guān)
func makeUIView(context: Context) -> UISegmentedControl {
let segmentControl = UISegmentedControl()
segmentControl.insertSegment(withTitle: "紅", at: 0, animated: true)
segmentControl.insertSegment(withTitle: "黃", at: 1, animated: true)
segmentControl.insertSegment(withTitle: "藍(lán)", at: 2, animated: true)
segmentControl.selectedSegmentIndex = selectedSegmentIndex
// 注意這里的參數(shù),與Coordinator有關(guān)
segmentControl.addTarget(
context.coordinator,
action: #selector(Coordinator.updateCurrentPage(sender:)),
for: .valueChanged)
return segmentControl
}
func updateUIView(_ uiView: UISegmentedControl, context: Context) {
uiView.selectedSegmentIndex = selectedSegmentIndex
}
// 協(xié)議的方法之一,返回一個(gè)協(xié)調(diào)器
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// 自定義協(xié)調(diào)器,UIKit與SwiftUI交互的地方
class Coordinator: NSObject {
var control: SegmentControl
init(_ control: SegmentControl) {
self.control = control
}
@objc func updateCurrentPage(sender: UISegmentedControl) {
control.selectedSegmentIndex = sender.selectedSegmentIndex
}
}
}
struct ContentView : View {
@State private var selectedSegmentIndex: Int = 1
var body: some View {
SegmentControl(selectedSegmentIndex: $selectedSegmentIndex)
}
}
UIViewControllerRepresentable
- 要使 UIViewController 在 SwiftUI 中可用,需要用
UIViewControllerRepresentable對 UIViewController 進(jìn)行包裝。 -
UIViewControllerRepresentable中主要有兩個(gè)方法需要實(shí)現(xiàn):-
makeUIViewController:創(chuàng)建UIViewController。 -
updateUIViewController:根據(jù)條件和業(yè)務(wù)邏輯設(shè)置UIViewController的狀態(tài)。
-
- 案例
import SwiftUI
import UIKit
struct NavigationViewController: UIViewControllerRepresentable {
var vc: UIViewController
var title: String
func makeUIViewController(context: Context) -> UINavigationController {
let nvc = UINavigationController(rootViewController: vc)
return nvc
}
func updateUIViewController(_ navigationController: UINavigationController, context: Context) {
navigationController.viewControllers[0].title = title
}
}
struct ContentView : View {
var body: some View {
NavigationViewController(vc: UIViewController(), title: "UIViewControllerRepresentable")
}
}
SwiftUI in UIKit
SwiftUI 中的 View 需要使用UIHostingController包裝以后才可以給 UIKit 使用。
UIHostingController
在開發(fā) iOS 項(xiàng)目章節(jié)已經(jīng)分析過啟動(dòng)流程,就是通過UIHostingController包裝 ContentView,然后賦值給window.rootViewController。
// 可以是復(fù)雜的ContentView
let vc = UIHostingController(rootView: ContentView())
// 也可以時(shí)簡單的Text等其他View
let vc = UIHostingController(rootView: Text("Hello SwiftUI"))