SwiftUIとUIKitを組み合わせた場合の画面遷移について
こんにちは。
たまにiOS開発を行なっており、UIKit や Storyboard によるUI開発が大変に感じており、影響のない範囲で SwiftUI による実装を組み合わせていました。
その際に、SwiftUI で作成した View とUIKit(UIViewController/Storyboard)で作成した View の画面遷移について、個人的にわかりづらいと感じたので備忘録としてまとめます。
注意:個人的には、依然として SwiftUI 制限があるため、可能な限り混在させないというのは大事かなと思っています。
(ただ、SwiftUI による実装のしやすさを考えると使いたくなる...)
UIKitのViewからSwiftUIのViewに遷移する
この場合は、UIHostingController を利用します。
引数 rootView に、SwiftUI で作成した View を指定します。
UIHostingVontroller で得た View に対して、modalPresentationStyle の指定や UIKit で使われる present() を用いて遷移することができました。
let view = UIHostingController(rootView: ContentView())
view.modalPresentationStyle = .fullScreen
self.present(view, animated: true)
SwiftUIのViewからUIKitのViewに遷移する
この場合、UIHostingController のように UIViewController/Storyboard で実装した View を呼び出すことのできる関数は見つかりませんでした。
調べてみると、SwiftUI で作成した View 上に UIViewController のオブジェクトを作成できる UIViewControllerRepresentable と呼ばれるプロトコルがあったので、こちらを用いて UIKit で作成した View を呼び出します。
まず、SwiftUI 上で画面遷移の部分を用意します。今回は、ボタンを押したときに遷移するようにします。
@State private var isPresented: Bool = false
var body: some View {
Button(action: {
isPresented = true
}) {
Text("Return")
}
.fullScreenCover(isPresented: $isPresented) {
// ここで、UIViewControllerRepresentableによるインスタンスを呼び出す //
}
}
次に、UIViewControllerRepresentable インスタンスを作成します。このインスタンスは、作成したときに1度だけ呼び出される makeUIViewController() と、View を更新した場合に呼び出す updateUIViewController() で構成されています。
今回は、ボタンが押されたときにインスタンスを作成し、画面遷移するだけなので、makeUIViewController() にて実装します。Return Value の ViewController は、UIKit で作成した遷移先の UIViewController です。あとは、UIKit と同じように、Storyboard/UIViewController による画面遷移です。
struct ViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> ViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "vc") as! ViewController
vc.modalPresentationStyle = .overFullScreen
return vc
}
func updateUIViewController(_ uiViewController: ViewController, context: Context) {
}
}
このインスタンスを、先ほどの .fullScreenCover にて作成します。
@State private var isPresented: Bool = false
var body: some View {
Button(action: {
isPresented = true
}) {
Text("Return")
}
.fullScreenCover(isPresented: $isPresented) {
ViewControllerRepresentable()
}
}
これで SwiftUI で作成した View から UIKit で作成した View に遷移できるようになりました。
複雑にみえますが、そもそも UIViewControllerRepresentable は UIKit の View や機能を SwiftUI の View に入れたい場合に利用されるラッパーのようなものなので、使い方としては順当かもしれません。
まとめ
最初にも書いたように SwiftUI に制限があるため、このようにお互いをラッパーするような形で使うことになると思います。ですが、SwiftUI で実装することでやりやすいこともあるため、参考になればと思いまとめました。
このあとの動向も見ていきたいと思います。
参考にさせていただきました。
ChatGPTに聞いてみました。
質問:SwiftUI で作成した View と UIKit で作成した View が共存する状況において、SwiftUI で作成した View からすでに作成済みである UIKit で作成した View に画面遷移する方法をコードで教えてください。
何か参考にしたリファレンスがあるか聞きました。
改めて凄さを感じました。。。