LoginSignup
9
12

More than 1 year has passed since last update.

SwiftUI内でUIViewControllerを使おう

Last updated at Posted at 2022-01-13

概要

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)
        }
    }
}

もちろんこれはプレビューでも動作し、広告が表示されます。

スクリーンショット 2022-01-13 17.57.43.png

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)
    }
}

右上あたりの+ボタンから追加できるようになります。

スクリーンショット 2022-01-13 17.54.05.png

9
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
12