雖然 SwiftUI 已經問世五年了 (WWDC2019),但實務上寫 iOS 的碼農,大都還在維護 Objective-C 撰寫的專案吧!尤其是陳年老 code,不像是美酒一樣越陳越香,反而是越陳越覺得難以吞嚥,幻想如果有一天全都變成漂亮的靚女 (Swift) ,而且全都用 SwiftUI 來化妝,那該有多好啊!
蘋果教主聽到大叔們心裡的呼喊,派了 UIHostingController
仙女下凡,來滿足一眾死肥宅們的春夢,在龍鐘老 code 中,嵌入 SwiftUI 這位纖纖美少女。
STEP1 先來產生 SwiftUI
在 Objective-C 的專案中 New File,大膽的選擇 SwiftUI View
貼心的 Xcode,會詢問您要不要產生一個 Bridging Header 的標頭檔 ( 如果專案沒產生過,你也可在專案設定中自行設定它的名字 ),這個檔主要是拿來做 Objective-C 與 Swift 溝通的橋樑,在這 .h 中 import Objective-C 寫的 .h,就可以在 Swift 中,呼叫 OC ( Objective-C ) 寫的函式 (function)。
如下的 SwiftUI
import SwiftUI
struct HelloView: View {
var body: some View {
ZStack {
Color.blue.opacity(0.6)
.ignoresSafeArea()
Text("Hello, Objective-C")
.font(.title)
.foregroundStyle(.white)
}
}
}
#Preview {
HelloView()
}
寫 SwiftUI 的好處,就是在畫面右邊可以即時見到 UI 的渲染,不用像盲劍客一樣,全靠想像來寫 code 畫 UI 了。
STEP2 再來產生一 View Controller ( UIKit )
選擇由 UIViewController 繼承,語言 Swift
如下 Swift
import UIKit
import SwiftUI
class HelloViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let helloView = HelloView()
// the key:UIHostingController is the bridge between UIKit and SwiftUI
let hostCtr = UIHostingController(rootView: helloView)
// add this hosting controller to our view controller
addChild(hostCtr)
view.addSubview(hostCtr.view)
// set auto layout constraints of hosting controller
hostCtr.view.translatesAutoresizingMaskIntoConstraints = false
hostCtr.view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
hostCtr.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
hostCtr.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
hostCtr.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0).isActive = true
// notify hosting controller that has been added to parent view controller
hostCtr.didMove(toParent: self)
}
}
這 view controller 的目的,就是作為 SwiftUI View 的載具,本身 HelloViewController 繼承 UIKit 框架的 UIViewController,在 viewDidLoad 中,使用蘋果仙女 UIHostingController 來初始化我們設計的 HelloView,初始化完成後,再用平常 UIKit 加 subview controller 的方法,把仙女加進 HelloViewController 中。
STEP3 Objective-C 登場
要讓 OC 使用 Swift 撰寫的 code 很簡單,只要在 OC 的 .m 中,import 以下兩個 .h 即可:
#import "WelcomeSwiftUI-Bridging-Header.h"
#import "WelcomeSwiftUI-Swift.h"
第一個 .h 就是在步驟一 Xcode 生成的 Bridging Header 檔,而第二個 .h 則是 Swift 會自動生成的標頭檔,目的是給 OC 看的,讓 OC 可以藉此呼叫 Swift 寫的函式 (function)
Importing Swift into Objective-C | Apple Developer Documentation
STEP4 在 Objective-C 裡使用 Swift
終於可以在 OC 的 view 中貼上 SwiftUI view 了,如範例在 OC storyboard 中的 view container,直接就可以設定成 swift 寫的 HelloViewController
執行結果:view controller 的上半部是用 OC ( UIKit ) ,下半部是 Swift ( SwiftUI ) 。酷 😎
參考專案:theLittleApps/WelcomeSwiftUI
References
=> Integrating SwiftUI into Objective-C Projects: Two Effective Approaches
=> How to Use Child ViewControllers (Container Views) in Swift - Programmatic & Storyboard
=> UIHostingController | Apple Developer Documentation