(Swift) 混合架構開發:將 SwiftUI 設為 Storyboard 的初始畫面

使用 SwiftUI 來建構畫面的速度非常快,相較於以往的 Storyboard,它省去了許多 AutoLayout 的設定以及元件的拖放。在大多數的情況下我們仍需要使用 Storyboard 來建構 APP 的畫面。但有些畫面也許使用 SwiftUI 來建構畫面會更為方便。這時就需要在原本的 StoryBoard 中新增一個 SwiftUI 來進行畫面建構,而這一篇最主要的問題就是『如何將SwiftUI 所建構的畫面設為 Storyboard 的初始畫面』。

目錄:

SwiftUI 基本的語法
UIHostingController
使用 Button 切換到 SwiftUI 畫面(IBAction 觸發)
使用 Button 切換到 SwiftUI 畫面(IBSegueAction 觸發)
將 SwiftUI 畫面設為開啟 APP 的的第一個畫面
在初始畫面是 SwiftUI 的情況下,跳轉到 Storyboard 中其它畫面

SwiftUI 基本的語法

UIHostingController

當需要將 SwiftUI 視圖整合到 UIKit 中,可以建立 UIHostingController 物件並指定根視圖。這樣就可以隨時變更 rootView 屬性,並像使用其他視圖控制器一樣使用它

使用 Button 切換到 SwiftUI 畫面(IBAction 觸發

在 Storyboard 新增一個 SwiftUI View -> SwiftUIView

import SwiftUI

struct SwiftUIView: View {

var body: some View {
ZStack {
Color(red: 155/255, green: 190/255, blue: 200/255)
.ignoresSafeArea()
Text("This is SwiftUI View")
.font(.largeTitle)
.foregroundColor(.white)
}
}
}

#Preview {
SwiftUIView()
}

回到 Stroyboard 在 ViewController 新增一個 Button(SwiftUI) ,並拉 @IBAction func showSwiftUI ,記得要 import SwiftUI

    @IBAction func showSwiftUI(_ sender: UIButton) {
let contentView = SwiftUIView()
let controller = UIHostingController(rootView: contentView)
present(controller, animated: true, completion: nil)
}

改成全頁顯示

        controller.modalPresentationStyle = .fullScreen

使用 Button 切換到 SwiftUI 畫面(IBSegueAction 觸發)

在畫面新增 UIHostingController 也可以做到相同的效果

   @IBSegueAction func showswiftUI(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: SwiftUIView())
}

將 SwiftUI 畫面設為開啟 APP 的的第一個畫面

由於在 Storyboard 通常我們會在 Main 裡面選擇一個 Controller,並點選右側的 View Controller 中的 is initial View Controller,來當作畫面初始的 Controller,但是透過上面的兩種方建立 SwiftUI 顯然是不能開啟 APP 時,就跳轉到 SwiftUI 畫面,這時候就需要 Container View 來幫助我們

在 ViewController 新增一個 Container View ,並設定 AutoLayout,最後產生出的 ViewController 將其刪除

接下來在畫面新增 UIHostingController,並從 Container View 按下右鍵拖移到 UIHostingController ,選取 Embed

再來就是拉 @IBSegueAction

    @IBSegueAction func showswiftUI(_ coder: NSCoder) -> UIViewController? {
return UIHostingController(coder: coder, rootView: SwiftUIView())
}

這樣一來初始的畫面就會是 SwiftUI 所建構的畫面

在初始畫面是 SwiftUI 的情況下,跳轉到 Storyboard 中其它畫面

例:一開始的登入畫面採用 SwiftUI 來建構,登入成功後跳轉到 Storyboard 中其它的 ViewController

就如剛開始前面提到的一樣,雖然使用 SwiftUI 建構畫面是相當快速的,但是採用 混合架構開發,當然也會遇到蠻多問題要解決的,你可以想像平常如何在 Storyboard 中切換畫面,最常使用的是按下 Button 後轉到下一個畫面,或是使用 present 或 show 來進行畫面轉換,但在使用 SwiftUI 的情況下,顯然這些方法在 SwiftUI 底下是行不通的,所以這時後就需要 Notification 來幫助我們

SwiftUIView 新增一個 Button

import SwiftUI

struct SwiftUIView: View {

var body: some View {
ZStack {
Color(red: 155/255, green: 190/255, blue: 200/255)
.ignoresSafeArea()
VStack(spacing: 20) {
Text("This is SwiftUI View")
.font(.largeTitle)
.foregroundColor(.white)
Button {

} label: {
Text("showSecondViewController")
}
}
}
}
}

#Preview {
SwiftUIView()
}

新增一個 Swift File Notification

import Foundation

struct AllNotification {
static let toSecondViewController = Notification.Name("toSecondViewController")
}

在 Storyboard 新增一個 SecondViewController

現在要做就是在 SwiftUIView 點選 Button showSecondViewController 後,跳轉到 SecondViewController

SwiftUIView 中的 Button 底下發布通知

                Button {
// 發布通知
NotificationCenter.default.post(name: AllNotification.toSecondViewController, object: nil)
} label: {
Text("showSecondViewController")
}

SecondViewController,並設定 Storyboard ID -> SecondViewController

ViewController 新增以下的 function toSecondViewController

    @objc func toSecondViewController(noti: Notification) {
let sb = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
sb.modalPresentationStyle = .fullScreen
present(sb, animated: true, completion: nil)
}

最後在 viewDidLoad 新增 接收通知,大致上就完成了

    override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// 接收通知
NotificationCenter.default.addObserver(self, selector: #selector(toSecondViewController(noti: )), name: AllNotification.toSecondViewController, object: nil)
}

Reference

--

--