SwiftUIのTabViewでUIKitと連携する時の罠
SwiftUIのTabViewでUIKitと連携する時にハマってしまった部分があったので、その内容と回避策を共有したい。
事象
今回はTabViewで既存のUIViewControllerを表示したかったので、UIViewControllerRepresentableに準拠するViewを定義してTabViewで表示した。
なお、わかりやすいようにUIViewController.view.backgroundColorをRGBで設定しており、UIViewControllerWrapperにも.background(.gray)を設定している。
struct ContentView: View {
let redViewController = UIViewController()
let greenViewController = UIViewController()
let blueViewController = UIViewController()
var body: some View {
TabView {
UIViewControllerWrapper(viewController: redViewController)
.background(.gray)
.ignoresSafeArea()
UIViewControllerWrapper(viewController: greenViewController)
.background(.gray)
.ignoresSafeArea()
UIViewControllerWrapper(viewController: blueViewController)
.background(.gray)
.ignoresSafeArea()
}
.tabViewStyle(.page)
.onAppear {
redViewController.view.backgroundColor = .red
greenViewController.view.backgroundColor = .green
blueViewController.view.backgroundColor = .blue
}
.ignoresSafeArea()
}
}
struct UIViewControllerWrapper: UIViewControllerRepresentable {
private let viewController: UIViewController
init(viewController: UIViewController) {
self.viewController = viewController
}
func makeUIViewController(context: Context) -> some UIViewController {
return viewController
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {}
}
これを実行すると、冒頭のGIFにあるように、縦画面では問題なくページングできるが、2枚目の画面で横回転して最初の画面に戻ってさらにページングすると、UIViewController.viewの背景色を.redに設定しているにも関わらず、それが表示されずUIViewControllerWrapperの.grayしか表示されていないことがわかる。
View Hierarchyで確認しても、正常に表示されている時(右側)と違って、UIViewController自体が消えている(左側)ことがわかる。
この事象の原因は不明だが、SwiftUIでUIViewControllerRepresentableの使用時のバグだと思われる。ちなみに、TabViewで表示するUIViewControllerWrapperを2つにすると問題がなく、4つにすると3枚目の画面から2枚目の画面に戻る時でも発生した。
回避策
上記の通り原因は不明だが、UIViewController自体が消えていたので、UIViewControllerRepresentableをUIViewRepresentableに変更して、makeUIView(context:)でUIViewController.viewを返却するようにしたところ、この事象は発生しなくなった。
struct UIViewWrapper: UIViewRepresentable {
private let viewController: UIViewController
init(viewController: UIViewController) {
self.viewController = viewController
}
func makeUIView(context: Context) -> some UIView {
return viewController.view
}
func updateUIView(_ uiView: UIViewType, context: Context) {}
}
同じような事象に遭遇された場合は、この方法を試してくみるといいかもしれません。
サンプルコード
この記事が気に入ったらサポートをしてみませんか?