在目前階段,SwiftUI 很難獨立開發一款功能強大的 App,還是需要與 UIKit 一起合作,借助 UIKit 成熟完善的知識體系,二者互相嵌套形成混合開發。
UIKit in SwiftUI
UIKit | SwiftUI |
---|---|
UIView | UIViewRepresentable |
UIViewController | UIViewControllerRepresentable |
UIViewRepresentable
- 要使 UIView 在 SwiftUI 中可用,需要用
對 UIView 進行包裝。UIViewRepresentable
-
中主要有兩個方法需要實作:UIViewRepresentable
-
:建立makeUIView
。View
-
:根據條件和業務邏輯設定updateUIView
的狀态。View
-
- 案例一
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()
}
}
複制
- 案例三
// 定義一個類負責實作代理
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: "請輸入内容").frame(height: 40).padding()
}
}
複制
- 如果要橋接 UIKit 的資料綁定(Delegate,Target/Action),需要使用
進行協調。Coordinator
import SwiftUI
import UIKit
// 自定義個SegmentControl控件
struct SegmentControl: UIViewRepresentable {
@Binding var selectedSegmentIndex: Int
// 下面兩個方法都是和 UIKit 相關
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: "藍", at: 2, animated: true)
segmentControl.selectedSegmentIndex = selectedSegmentIndex
// 注意這裡的參數,與Coordinator有關
segmentControl.addTarget(
context.coordinator,
action: #selector(Coordinator.updateCurrentPage(sender:)),
for: .valueChanged)
return segmentControl
}
func updateUIView(_ uiView: UISegmentedControl, context: Context) {
uiView.selectedSegmentIndex = selectedSegmentIndex
}
// 協定的方法之一,傳回一個協調器
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// 自定義協調器,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 中可用,需要用
對 UIViewController 進行包裝。UIViewControllerRepresentable
-
中主要有兩個方法需要實作:UIViewControllerRepresentable
-
:建立makeUIViewController
。UIViewController
-
:根據條件和業務邏輯設定updateUIViewController
的狀态。UIViewController
-
- 案例
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
在開發 iOS 項目章節已經分析過啟動流程,就是通過
UIHostingController
包裝
ContentView
,然後指派給
window.rootViewController
。
// 可以是複雜的ContentView
let vc = UIHostingController(rootView: ContentView())
複制
// 也可以時簡單的Text等其他View
let vc = UIHostingController(rootView: Text("Hello SwiftUI"))
複制