SwiftUI - 與UIKit集成

說到與UIKit的集成不免會(huì)覺得有些雞肋乙埃,因?yàn)楝F(xiàn)在很難做到只支持iOS13瀑构,不過到iOS14時(shí),這種集成就變得必不可少了吧惯豆,在此先預(yù)熱一下咯 ~ 先想想使用場(chǎng)景:

  1. 在現(xiàn)有基于UIKit的App中使用SwiftUI - 這應(yīng)該是最常見的一種方式蠢熄;
  2. 在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埋同,如何使用呢州叠?

  1. 添加內(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)
  }
}
  1. 實(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í)候再來重溫咯~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末集嵌,一起剝皮案震驚了整個(gè)濱河市萝挤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌根欧,老刑警劉巖怜珍,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異凤粗,居然都是意外死亡酥泛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門嫌拣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柔袁,“玉大人,你說我怎么就攤上這事亭罪∈葩桑” “怎么了歼秽?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵应役,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)箩祥,這世上最難降的妖魔是什么院崇? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮袍祖,結(jié)果婚禮上底瓣,老公的妹妹穿的比我還像新娘。我一直安慰自己蕉陋,他們只是感情好捐凭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凳鬓,像睡著了一般茁肠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缩举,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天垦梆,我揣著相機(jī)與錄音,去河邊找鬼仅孩。 笑死托猩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的辽慕。 我是一名探鬼主播京腥,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼溅蛉!你這毒婦竟也來了绞旅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤温艇,失蹤者是張志新(化名)和其女友劉穎因悲,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勺爱,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晃琳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了琐鲁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卫旱。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖围段,靈堂內(nèi)的尸體忽然破棺而出顾翼,到底是詐尸還是另有隱情,我是刑警寧澤奈泪,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布适贸,位于F島的核電站灸芳,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏拜姿。R本人自食惡果不足惜烙样,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蕊肥。 院中可真熱鬧谒获,春花似錦、人聲如沸壁却。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽展东。三九已至精耐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琅锻,已是汗流浹背卦停。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恼蓬,地道東北人惊完。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像处硬,于是被迫代替她去往敵國(guó)和親小槐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容