UIHostingControllerのすすめ
UIHostingControllerのすすめ
はじめに
SwiftUI
を使ってみてレイアウトとかはすごく楽になった気がするのですが画面遷移でいろいろつまずくことがありました。
- 2画面前に戻りたいとか
-
@Binding
の値更新すると画面閉じちゃうとか - ...etc
画面遷移だけ UIKit
でやるといい感じというのを前に聞いたので UIHostingController
を使って実際にやってみました。
実装
View
のデータ更新を HostingController
からできるやつを作ります。
Storyboard を使ってるので init(coder:)
で View
を生成しています。
import SwiftUI
import Combine
final class HogeHostingController: UIHostingController<HogeView> {
private let viewModel = HogeViewModel()
private var subscriptions = Set<AnyCancellable>()
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder, rootView: .init(viewModel: viewModel))
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel.text = "ほげ"
viewModel.subject.sink {
print("ボタン押下")
}.store(in: &subscriptions)
}
}
final class HogeViewModel: ObservableObject {
var text: String = ""
let buttonTitle = "ぼたん"
let subject = PassthroughSubject<Void, Never>()
}
struct HogeView: View {
@ObservedObject var viewModel: HogeViewModel
var body: some View {
VStack {
Text(viewModel.text)
Button(viewModel.buttonTitle) {
viewModel.subject.send()
}
}
}
}
上記を実行すると無事、画面に「ほげ」と表示されます👏
HostingController
と View
が ViewModel
を共有しており、 ViewModel
の値を更新すれば View
も更新されるようになっています。ボタン押下などのイベントは sink
で HostingController
で処理できるようになっています。
失敗例
HostingController
から View
を更新するためにいろいろ試してみましたが下記実装では無理でした。
Stateを使うその1
以下のようにすると値は更新されず画面には「ほげ」と表示されます。
import SwiftUI
final class HogeHostingController: UIHostingController<HogeView> {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder, rootView: .init())
}
override func viewDidLoad() {
super.viewDidLoad()
rootView.text = "ふが"
}
}
struct HogeView: View {
@State var text = "ほげ"
var body: some View {
Text(text)
}
}
遅延実行とか viewDidAppear
で更新とかもダメでした。View
側に update
メッソッドを作りその中で @State
を更新するという方法もダメでした。
Stateを使うその2
HostingController
に @State
を持たして View
側に @Binding
で渡せばいけるか?と思いましたがダメでした。
無理やりやると実行時にこういう警告が出ます。
[SwiftUI] Accessing State's value outside of being installed on a View. This will result in a constant Binding of the initial value and will not update.
おわりに
ObservableObject
を使うことで無事 HostingController
から View
を更新できました🎉
Discussion