雖然 SwiftUI 已經問世五年了 (WWDC2019),但實務上寫 iOS 的碼農,大都還在維護 Objective-C 撰寫的專案吧!尤其是陳年老 code,不像是美酒一樣越陳越香,反而是越陳越覺得難以吞嚥,幻想如果有一天全都變成漂亮的靚女 (Swift) ,而且全都用 SwiftUI 來化妝,那該有多好啊!

01_objectiiveC_meet_swiftUI

蘋果教主聽到大叔們心裡的呼喊,派了 UIHostingController 仙女下凡,來滿足一眾死肥宅們的春夢,在龍鐘老 code 中,嵌入 SwiftUI 這位纖纖美少女。


STEP1 先來產生 SwiftUI

Objective-C 的專案中 New File,大膽的選擇 SwiftUI View

02_create_swiftui

貼心的 Xcode,會詢問您要不要產生一個 Bridging Header 的標頭檔 ( 如果專案沒產生過,你也可在專案設定中自行設定它的名字 ),這個檔主要是拿來做 Objective-CSwift 溝通的橋樑,在這 .himport Objective-C 寫的 .h,就可以在 Swift 中,呼叫 OC ( Objective-C ) 寫的函式 (function)。

03_generate_bridge_file

如下的 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 了。

04_program_in_SwiftUI


STEP2 再來產生一 View Controller ( UIKit )

05_select_cocoa_touch

選擇由 UIViewController 繼承,語言 Swift

06_generate_uiviewctr

如下 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,初始化完成後,再用平常 UIKitsubview 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

07_container_view

執行結果:view controller 的上半部是用 OC ( UIKit ) ,下半部是 Swift ( SwiftUI ) 。酷 😎

08_simulator_result

參考專案: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


贊助我們

Buy Me A Coffee