はじめに
SwiftUIのAPIが年々充実していく中で、UIKit
から SwiftUI
への切り替えを考えられている方も多いのではないでしょうか?
そんな中で、今年のWWDC2022にて、Use SwiftUI with UIKitという、UIKitからSwiftUIへの置き換えについて語られているセッションがありました。
今回は、そのセッションの内容を元に、UIKit
から SwiftUI
への置き換え方法を見ていきたいと思います。
UIHostingControllerを使う
iOS13から既に提供されている方法です。
SwiftUI
のView
をUIViewController
に格納することでSwiftUI
をUIKit
に取り込んで表示します。
/// SwiftUI View
let rootView = RootView()
let hostingController = UIHostingController(rootView: rootView)
present(hostingController, animated: true)
UIHostingController
を使用する場合、UIKit
とSwiftUI
間でのデータの受け渡し方法についてはいくつかやり方があリます。
まず一つ目は、データ変更時は手動でUIHostingController
のrootView
を更新する方法です。
シンプルではありますが、毎回手動で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
を利用する仕組みで、余分なView
やViewController
を介さずにSwiftUI
を使ったカスタムセルを表示することができます。
使い方は簡単で、UITableViewCell
またはUICollectionViewCell
のcontentConfiguration
に対し、UIHostingConfiguration
のcontent
引数で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
に置き換えていく際には大活躍してくれそうな気がしております!
また、何かご意見等ありましたら、コメントいただけますと幸いです。
どうぞよろしくお願いいたします。