概要
SwiftUIがだんだんと使われてきている印象を受けますが、まだまだ世間ではUIViewControllerが使われているのも事実です。
ただ単にUIKitベースの画面を表示したいのであれば UIViewControllerRepresentable
などを使えばいいのですが、
特にサードパーティがリリースしている広告SDKなどはUIViewControllerが必要な実装が行われているので、SwiftUI内で広告を出そうとする場合、工夫が必要になります。
SwiftUI内でUIViewControllerを取得
なるべくSwiftUIっぽくViewControllerを使おうと考え、以下のような形になりました。
ContainerViewがViewControllerを返してくれて、それを使って広告など、ViewControllerが必要なビューを表示していきます。
struct SwiftUIView: View {
var body: some View {
ContainerView { viewController in
// UIViewControllerが必要な広告を、UIViewRepresentableで実装
AdView(viewController: viewController)
}
}
}
もちろんこれはプレビューでも動作し、広告が表示されます。
ContainerViewの実装
ViewControllerを返すContainerViewの実装です。
内部はUIViewControllerRepresentableを実装しています。
struct ContainerView<Content>: UIViewControllerRepresentable where Content : View {
let content: (UIViewController) -> Content
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIViewController {
let vc = UIHostingController(rootView: AnyView(EmptyView()))
vc.rootView = AnyView(content(vc))
return vc
}
func updateUIViewController(_ viewController: UIViewController, context: Context) {
viewController.rootView = AnyView(content(viewController))
}
class Coordinator: NSObject {
var parent: ContainerView
init(_ viewController: ContainerView) {
parent = viewController
}
}
}
キモとなるのは makeUIViewController(context:)
の部分で、一度ベースとなるUIHostingControllerをEmptyViewで作成しておいてViewControllerインスタンスを作った後、改めてパラメータのcontentをあてがう実装になってます。
これでContainerViewの内部はcontentのUIになり、SwiftUI側にはViewControllerが返されます。
ライブラリに登録
UIViewControllerをしょっちゅう使う場合は、このContainerViewをライブラリに登録しておくと便利です。
import DeveloperToolsSupport
import SwiftUI
struct UIKitViewContent: LibraryContentProvider {
var views: [LibraryItem] {
LibraryItem(ContainerView(content: { _ in EmptyView() })
,title: "ViewController container"
,category: .layout)
}
}
右上あたりの+ボタンから追加できるようになります。