在 UIKit 使用 SwiftUI

· 2min

前言

如何在 UIKit 使用 SwiftUI, 目前大概分為三種, 分別使用

  1. UIHostingController
  2. _UIHostingView
  3. UIHostingConfiguration

官方主推 UIHostingController, 而 UIHostingConfiguration 目前使用於 UITableView 與 UICollectionView 的 Cell.

至於 _UIHostingView 目前雖為 Public, 難保官方會把它變為 Private.

使用方式

UIHostingController

使用方式與一般 UIViewController 相同, ex:

let hostVC = UIHostingController(rootView: SomeSwiftUI())
hostVC.sizingOptions = [.intrinsicContentSize]
vc.addChild(hostVC)

hostVC.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostVC.view)
NSLayoutConstraint.activate([
    hostVC.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    hostVC.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])

hostVC.didMove(toParent: vc)

_UIHostingView

使用方法與一般 UIView 相同, 只是初始參數需帶入 SwiftUI, ex:

let view1 = _UIHostingView(rootView: SomeSwiftUI())
view1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view1)
NSLayoutConstraint.activate([
    view1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    view1.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])

UIHostingConfiguration

透過 ViewBuilder 初始 SwiftUI, 並使用 makeContentView 返回 UIView, ex:

let view1 = UIHostingConfiguration {
    SomeSwiftUI()
}.margins(.all, 0)
 .makeContentView()

view1.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(view1)
NSLayoutConstraint.activate([
    view1.centerXAnchor.constraint(equalTo: view.centerXAnchor),
    view1.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])

已知問題

在 UIKit 使用 SwiftUI, 目前最大的問題是, 當 SwiftUI content 變動時,
UIView 的 Size 不會跟著變動, 如圖:

image

UIHostingController 在 iOS 16 引入參數 sizingOptions = [.intrinsicContentSize] 可解決此問題.
UIHostingConfiguration 在 Cell 裡不會產生此問題, 但在一般 UIView 裡會產生, 且無法自動變動 Size.
_UIHostingView 也無法自動變動 Size.

可嘗試使用第三方套件 HostingView.

參考

HostingView - A cleaner way to embed SwiftUI in your UIKit projects