說到與UIKit的集成不免會(huì)覺得有些雞肋乙埃,因?yàn)楝F(xiàn)在很難做到只支持iOS13瀑构,不過到iOS14時(shí),這種集成就變得必不可少了吧惯豆,在此先預(yù)熱一下咯 ~ 先想想使用場(chǎng)景:
- 在現(xiàn)有基于UIKit的App中使用SwiftUI - 這應(yīng)該是最常見的一種方式蠢熄;
- 在SwiftUI中使用UIKit - 新寫的頁面是用了SwiftUI跪解,但不免會(huì)跳轉(zhuǎn)到原先的頁面炉旷,或是在SwiftUI要使用UIKit寫的一些View签孔,畢竟重寫原先所有的view成本有些大,需要逐步替換窘行。
UIKit中使用SwiftUI
還記得我們?cè)?a href="http://www.reibang.com/p/2569e3bd04c2" target="_blank">SwiftUI 初識(shí) 中提到的UIHostingController
饥追,它是SwiftUI的容器,同時(shí)也是UIViewController的子類罐盔。集成方式也就顯而易見咯但绕。
@IBAction func showSwiftUIByCode(_ sender: Any) {
let vc = UIHostingController(rootView: ContentView())
self.show(vc, sender: nil)
}
若使用的是Segue
:
@IBSegueAction func openSwiftUI(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: ContentView())
}
在SwiftUI中使用UIKit
首先,我們來看看,如何從SwiftUI跳轉(zhuǎn)到一個(gè)UIViewController捏顺。先寫一個(gè)最簡(jiǎn)單的UIViewController:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
}
}
在SwiftUI中沒有提供類似self.show(vc, sender: nil)
的方式六孵,需要通過一個(gè)wrapper把ViewController包裝一下,可以認(rèn)為這就是一個(gè)適配器幅骄。SwiftUI提供了UIViewControllerRepresentable
協(xié)議劫窒,來承擔(dān)適配的功能,寫一個(gè)自己的ViewControllerRepresentation
實(shí)現(xiàn)這個(gè)協(xié)議拆座。
import SwiftUI
struct ViewControllerRepresentation: UIViewControllerRepresentable {
typealias UIViewControllerType = ViewController
func makeUIViewController(context: UIViewControllerRepresentableContext<ViewControllerRepresentation>) -> ViewControllerRepresentation.UIViewControllerType {
return UIStoryboard(name: "Storyboard", bundle: nil).instantiateViewController(identifier: "ViewController") as! ViewController
}
func updateUIViewController(_ uiViewController: ViewControllerRepresentation.UIViewControllerType, context: UIViewControllerRepresentableContext<ViewControllerRepresentation>) {}
}
其中的typealias UIViewControllerType = ViewController
指明了要包裹的UIViewController的具體類型主巍,并實(shí)現(xiàn)兩個(gè)方法,一個(gè)是make挪凑,一個(gè)是update孕索。其中make顧名思義,就是如何生成我們的ViewController躏碳,大家可以用自己喜歡的方式初始化它搞旭。update暫時(shí)放空,它的作用是如何更新這個(gè)VC唐断。我們會(huì)在下個(gè)示例中來看看它的用法选脊。
接下來,我們要在SwiftUI中來調(diào)用我們這里定義的ViewControllerRepresentation
脸甘。
var body: some View {
NavigationView {
VStack {
HStack {
TargetView(showAlert: $showAlert, rTarget: rTarget, gTarget: gTarget, bTarget: bTarget)
MatchingView(rGuess: $rGuess, gGuess: $gGuess, bGuess: $bGuess, counter: $counter.counter)
}
Button(action: { self.showAlert = true }) { Text("Hit me") }.alert(isPresented: $showAlert) { () -> Alert in
Alert(title: Text("Your Score"),message: Text(String(computeScore())))
}.frame(width: nil, height: 35, alignment: .center)
SlideView(value: $rGuess, textColor: .red)
SlideView(value: $gGuess, textColor: .green)
SlideView(value: $bGuess, textColor: .blue)
NavigationLink(destination: ViewControllerRepresentation()) {
Text("Play BullsEye").frame(width: nil, height: 31, alignment: .center)
}.padding(.bottom)
}
}
}
在底部我們放了一個(gè)NavigationLink
恳啥,它就是在NavigationView
中跳轉(zhuǎn)至其他頁面的一個(gè)鏈接容器,指明他的destination
就是我們剛剛定義好的ViewControllerRepresentation
丹诀,run一下钝的,大功告成。
此時(shí)铆遭,不免會(huì)想硝桩,如何傳遞參數(shù)呢?這個(gè)UIViewController如何將一下返回值回傳給SwiftUI呢枚荣?我們用下一個(gè)例子來說明碗脊,這個(gè)例子中是在SwiftUI中放置一個(gè)UIKit定義的UIView,先看code:
import SwiftUI
import UIKit
struct ColorUISlider: UIViewRepresentable {
var color: UIColor
@Binding var value: Double
typealias UIViewType = UISlider
func makeUIView(context: UIViewRepresentableContext<ColorUISlider>) -> UISlider {
let slider = UISlider(frame: .zero)
slider.thumbTintColor = color
slider.value = Float(value)
slider.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged(_:)), for: .valueChanged)
return slider
}
func updateUIView(_ uiView: UISlider, context: UIViewRepresentableContext<ColorUISlider>) {
uiView.value = Float(value)
}
class Coordinator: NSObject {
var value: Binding<Double>
init(value: Binding<Double>) {
self.value = value
}
@objc func valueChanged(_ sender: UISlider) {
self.value.wrappedValue = Double(sender.value)
}
}
func makeCoordinator() -> ColorUISlider.Coordinator {
return Coordinator(value: $value)
}
}
code有點(diǎn)多橄妆,我們一點(diǎn)點(diǎn)看衙伶。首先想用再SwiftUI中使用UIView,同樣需要一個(gè)適配器害碾,在UIViewController時(shí)矢劲,用了UIViewControllerRepresentable
,UIView提供了同樣的協(xié)議UIViewRepresentable
慌随,需要的方法也如出一轍:
typealias UIViewType = UISlider
func makeUIView(...)
func makeUIView(...)
typealias UIViewType = UISlider
指定了需要包裝的UIView的類型芬沉,make方法要求返回UIView的實(shí)例躺同,這里無需指定該view的frame或約束。
還記得我們之前留下的問題么丸逸?如何從SwiftUI中傳值給UIView(UIViewController是相同的)蹋艺,直接給這個(gè)ColorUISlider
添加屬性,通過struct的默認(rèn)構(gòu)造函數(shù)黄刚,便可以將參數(shù)傳入:
ColorUISlider(color: .red, value: .constant(0.5))
那么這個(gè)ColorUISlider
如何影響其父view呢车海?我們看到了這個(gè)@Binding參數(shù)@Binding var value: Double
(一個(gè)引用類型,如果不記得了可以查閱之前的一篇文章Data Binding)隘击,但Binding是沒有辦法在UIView中直接使用的侍芝,還需另一個(gè)類,Coordinator
埋同,如何使用呢州叠?
- 添加內(nèi)部類:
class Coordinator: NSObject {
var value: Binding<Double>
init(value: Binding<Double>) {
self.value = value
}
@objc func valueChanged(_ sender: UISlider) {
self.value.wrappedValue = Double(sender.value)
}
}
- 實(shí)現(xiàn)
UIViewRepresentable
的另一個(gè)方法makeCoordinator
:
func makeCoordinator() -> ColorUISlider.Coordinator {
return Coordinator(value: $value)
}
當(dāng)我們初始化UISlider的時(shí)候,給它添加了一個(gè)行為:
slider.addTarget(context.coordinator, action: #selector(Coordinator.valueChanged(_:)), for: .valueChanged)
在我們滑動(dòng)滑塊的時(shí)候凶赁,會(huì)觸發(fā):
@objc func valueChanged(_ sender: UISlider) {
self.value.wrappedValue = Double(sender.value)
}
從而使Binding起作用咧栗,影響外面?zhèn)魅氲?code>value,進(jìn)而作用于父View虱肄。
到此為止致板,我們集成SwiftUI的預(yù)熱已經(jīng)結(jié)束,還有很多細(xì)節(jié)沒有涉及咏窿,比如context斟或。畢竟是預(yù)熱嘛,用到的時(shí)候再來重溫咯~