天天看點

SwiftUI-混合開發

在目前階段,SwiftUI 很難獨立開發一款功能強大的 App,還是需要與 UIKit 一起合作,借助 UIKit 成熟完善的知識體系,二者互相嵌套形成混合開發。

UIKit in SwiftUI

UIKit SwiftUI
UIView UIViewRepresentable
UIViewController UIViewControllerRepresentable

UIViewRepresentable

  • 要使 UIView 在 SwiftUI 中可用,需要用

    UIViewRepresentable

    對 UIView 進行包裝。
  • 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 中可用,需要用

    UIViewControllerRepresentable

    對 UIViewController 進行包裝。
  • 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"))           

複制