SwiftUI-混合開發(fā)

在目前階段,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"))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 重點(diǎn)參考鏈接: View Programming Guide for iOS https://developer....
    Kevin_Junbaozi閱讀 4,712評論 0 15
  • 由于 API 變動(dòng),此文章部分內(nèi)容已失效,最新完整中文教程及代碼請查看 https://github.com/Wi...
    Willie_閱讀 8,112評論 29 23
  • 開始之前:請確保你的系統(tǒng)版本為macOS 10.15及以上版本且已經(jīng)安裝了Xcode 11。這種組合使您可以在Xc...
    Augs閱讀 3,130評論 0 7
  • 說到與UIKit的集成不免會(huì)覺得有些雞肋,因?yàn)楝F(xiàn)在很難做到只支持iOS13,不過到iOS14時(shí),這種集成就變得必不...
    西西的一天閱讀 7,268評論 0 13
  • 有點(diǎn)混亂的一天 昨晚小寶出汗厲害,一晚上換了四件衣服,頭發(fā)都濕透了。 是熱還是虛啊,真擔(dān)心。 前一晚跟她和哥哥睡床...
    winzy小文子閱讀 142評論 0 1

友情鏈接更多精彩內(nèi)容