0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UIKitからSwiftUI移行期に使うUIHostingControllerって何してるのか?

0
Posted at

前書き

最近私が開発しているプロジェクトでもようやく新規画面や一部既存画面でも
SwiftUIを用いた開発を行うとようになりました!

ただ漠然とUIHostingControllerというものを用いてUIKitとSwiftUIを共存させていたのですが
そもそもUIHostingControllerって何してるの?と思ったので調べてみました!

1. UIHostingControllerとは何か

結論から書くとUIHostingControllerは、SwiftUIのViewをUIKitが扱えるUIViewControllerとしてラップするためのクラスです。

SwiftUIのViewは単体ではUIKitのビュー階層に入れられません。UIKitはUIViewControllerやUIViewを期待しているからです。

  // これはできない
  let swiftUIView = MySwiftUIView()
  navigationController?.pushViewController(swiftUIView, animated: true) // :x: エラー

UIHostingControllerでラップすることで、SwiftUIのViewをUIKitの画面遷移に乗せられます:

  // これならOK
  let hostingController = UIHostingController(rootView: MySwiftUIView())
  navigationController?.pushViewController(hostingController, animated: true) // :white_check_mark:


2.なぜ必要なのか

SwiftUIは画面遷移やライフサイクル管理を担わない設計のため、既存のUIKitベースのナビゲーション構造と共存させる必要があるからです。

実務では:

  • 既存のナビゲーション構造(UINavigationController、UITabBarControllerなど)はそのまま使いたい

  • 新機能や画面単位でSwiftUIを導入したい

  • 既存のアーキテクチャ(Coordinator、RxFlowなど)を維持したい

    UIHostingControllerがあることで、画面単位で段階的にSwiftUI化ができます。

  // 既存のUIKit画面遷移の中に、SwiftUIの画面を1つだけ追加
  func navigateToNewFeature() {
      let swiftUIView = NewFeatureView()
      let hostingController = UIHostingController(rootView: swiftUIView)
      navigationController?.pushViewController(hostingController, animated: true)
  }

全部書き換えるのではなく、新しい画面から少しずつSwiftUIに移行していける。これがUIHostingControllerの存在意義です。


3. ライフサイクルはどうなる?

両方動きます。

UIHostingControllerはUIViewControllerのサブクラスなので、UIKitのライフサイクルメソッドが呼ばれます。同時に、中に入っているSwiftUI Viewのライフサイクル(onAppear / onDisappear)も動きます。

実際に確認してみましょう。

  // SwiftUI View
  struct SampleView: View {
      var body: some View {
          Text("Hello")
              .onAppear { print("🍎 SwiftUI: onAppear") }
              .onDisappear { print("🍎 SwiftUI: onDisappear") }
      }
  }

  // UIHostingController をサブクラス化
  class SampleHostingController: UIHostingController<SampleView> {
      override func viewDidLoad() {
          super.viewDidLoad()
          print("🍎 UIKit: viewDidLoad")
      }

      override func viewWillAppear(_ animated: Bool) {
          super.viewWillAppear(animated)
          print("🍎 UIKit: viewWillAppear")
      }

      override func viewDidAppear(_ animated: Bool) {
          super.viewDidAppear(animated)
          print("🍎 UIKit: viewDidAppear")
      }

      override func viewWillDisappear(_ animated: Bool) {
          super.viewWillDisappear(animated)
          print("🍎 UIKit: viewWillDisappear")
      }

      override func viewDidDisappear(_ animated: Bool) {
          super.viewDidDisappear(animated)
          print("🍎 UIKit: viewDidDisappear")
      }
  }
  実行結果(画面表示時):
  🍎 UIKit: viewDidLoad
  🍎 UIKit: viewWillAppear
  🍎 SwiftUI: onAppear
  🍎 UIKit: viewDidAppear

  実行結果(画面非表示時):
  🍎 UIKit: viewWillDisappear
  🍎 SwiftUI: onDisappear
  🍎 UIKit: viewDidDisappear

UIKitとSwiftUI、両方のライフサイクルがちゃんと動いていることが確認できました。

注意点

非表示時のonDisappearの順序は実行タイミングによって前後することがあります(viewWillDisappearの後だった
りviewDidDisappearの後だったり)。

厳密な順序に依存した実装は避けた方が安全です。

後書き

以上がUIHostingControllerとは何をしているのかなぜ必要なのかを記載させて頂きました、
まだまだUIKitを使用しているプロジェクト、これからSwiftUIに少しづつ切り替えていく予定のプロジェクトの方に参考になれば幸いです

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?