18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

UIKitを部分的にSwiftUIに置き換えていく方法について

Posted at

はじめに

SwiftUIのAPIが年々充実していく中で、UIKitから SwiftUIへの切り替えを考えられている方も多いのではないでしょうか?
そんな中で、今年のWWDC2022にて、Use SwiftUI with UIKitという、UIKitからSwiftUIへの置き換えについて語られているセッションがありました。
今回は、そのセッションの内容を元に、UIKitから SwiftUIへの置き換え方法を見ていきたいと思います。

UIHostingControllerを使う

iOS13から既に提供されている方法です。
SwiftUIViewUIViewControllerに格納することでSwiftUIUIKitに取り込んで表示します。

/// SwiftUI View
let rootView = RootView()
let hostingController = UIHostingController(rootView: rootView)

present(hostingController, animated: true)

UIHostingControllerを使用する場合、UIKitSwiftUI間でのデータの受け渡し方法についてはいくつかやり方があリます。

まず一つ目は、データ変更時は手動でUIHostingControllerrootViewを更新する方法です。
シンプルではありますが、毎回手動でrootViewの更新作業を行う必要があります。

/// SwiftUI
struct RootView: View {
    var text: String

    var body: some View {
        Text(text)
    }
}

/// UIKit
final class HostingController: UIViewController {
    let hostingController = UIHostingController<RootView>
    var text: String {
        // テキストの更新が入ったら、手動でRootViewの更新処理を走らせる
        didSet { update() }
    }

    func update() {
        hostingController.rootView = RooteView(text: text)
    }
}

2つ目に、@ObservedObjectを使う方法です。
UIKit側からSwiftUI側に@ObservedObjectを渡しておくことで、UIKit側で@ObservedObjectが更新されたらそれをSwiftUI側でで検知し更新が走ります。

/// ObservableObject
final class DataSource: ObservableObject {
    @Published var text: String = ""
}

/// SwiftUI
struct RootView: View {
    @ObservedObject var dataSource: DataSource
    var body: some View {
        Text(dataSource.text)
    }
}

/// UIKit
final class HostingController: UIViewController {
    let dataSource = DataSource()
    let hostingController = UIHostingController<RootView>

    override func viewDidLoad() {
        super.viewDidLoad()

        let rootView = RootView(dataSource: dataSource)
        hostingController = UIHostingController(rootView: rootView)
    }
}

基本的にはデータの受け渡しに関しては@ObservedObjectを使う形を採用するのが良いかと思います。

UIHostingConfigurationを使う

UIHostingConfigurationはiOS16からの新機能です。
既存の UITableViewまたはUICollectionViewの中でSwiftUIを利用する仕組みで、余分なViewViewControllerを介さずにSwiftUIを使ったカスタムセルを表示することができます。
使い方は簡単で、UITableViewCellまたはUICollectionViewCellcontentConfigurationに対し、UIHostingConfigurationcontent引数でSwiftUIのコードを入れて渡すだけです。

let cell: UITableViewCell

// SwiftUIのコードを直接書き込む
cell.contentConfiguration = UIHostingConfiguration(content: {
    Label("text", systemImage: "iamge_name")
        .font(.system(size: 12))           
})
.background(.white) // バックグラウンドのスタイルを設定
.margins(.horizontal, 16) // マージンを設定

// SwiftUI Viewを別途作成してそれを入れ込むことも可能
cell.contentConfiguration = UIHostingConfiguration(content: {
    LabelView()
        .swipeActions(content: { ... }) // Cellのスワイプ時のアクションを設定
})

struct LabelView: View {
    var body: some View {
        Label("text", systemImage: "iamge_name")
            .font(.system(size: 12))  
    }
}

また、セルのステータスに応じて処理を変えたい場合はconfigurationUpdateHandlerを使用することで、セルの状態変更にも柔軟に対応できます。

// セルの状態変更が起こるタイミングで都度呼ばれ、新規でUIHostingConfigurationを作成してcellに適用する
cell.configurationUpdateHandler = { cell, state in
    HStack {
        Label("text", systemImage: "iamge_name")
        Spacer()
        // セルが選択状態の時のみチェックマークを表示するようにする
        if state.isSelected {
            Image(systemName: "checkmark")
        }
    }
})

おわりに

iOS16から新たに登場したUIHostingConfigurationのおかげで、より簡単に、そしてより部分的にSwiftUIを部分的に導入していくことが可能になったかなと思います!
現状、iOS16以降でのみしか使用できないため、直近で使うことは難しいかもしれませんが、今後SwiftUIに置き換えていく際には大活躍してくれそうな気がしております!

また、何かご意見等ありましたら、コメントいただけますと幸いです。
どうぞよろしくお願いいたします。

参考

Use SwiftUI with UIKit - WWDC22 - Videos - Apple Developer

18
14
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
18
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?