概要
実行時に UIViewController を必要とする関数は少なくありません。
-
GoogleSignInライブラリのGIDSignIn.sharedInstance.signIn(with: , presenting: ) - AdMob のインタースティシャル広告表示時の
GADInterstitialAd.present(fromRootViewController: )
UIViewController が表立って使うことがない SwiftUI において、それらの関数を実行する際に UIApplication.shared.windows.first?.rootViewController を使う方法を紹介している記事が数多くあります。
しかし、これはあまり安全とは言えません。
今回は、UIApplication.shared.windows.first?.rootViewController を使わずにこれらの関数を使用する方法を紹介したいと思います。
参考記事
ざっくりとした流れ
ちょっとトリッキーですが、やり方はそこそこシンプルです
- 空の
UIViewControllerをUIViewControllerRepresentableでラップしたViewを作成 - SwiftUI の View に、前述の
Viewを初期化した変数 (仮にanchorViewとする) を追加 -
body内で、.backgroundに前述の変数を設置 -
UIViewControllerを必要とする関数に、anchorView.viewControllerを渡す
実際に書いてみる
- ここでは、例として AdMob のインタースティシャル広告を表示するコードを書いてみたいと思います。
-
空の
UIViewControllerをラップしたRepresentedEmptyViewControllerを作成import SwiftUI // MARK: - 空の `UIViewController` をラップしたもの struct RepresentedEmptyViewController: UIViewControllerRepresentable { let viewController: UIViewController = .init() func makeUIViewController(context: Context) -> some UIViewController { return self.viewController } func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) { } } -
UIViewControllerを必要とする関数を実行する View に、前述のRepresentedEmptyViewControllerを初期化した変数を追加import SwiftUI struct ContentView: View { // これ↓ let prensentationAnchorView: RepresentedEmptyViewController = .init() var body: some View { // 中略 } } -
body内のルートのビューに、.backgroundモディファイアを付与し、前述のprensentationAnchorViewを設置- background に設置することで、UIへの影響なく設置できます
import SwiftUI struct ContentView: View { let prensentationAnchorView: RepresentedEmptyViewController = .init() var body: some View { VStack { } // これ↓ .background { prensentationAnchorView } } } -
UIViewControllerを必要とする関数を使う処理を書く (ここではGADInterstitialAd.present(fromRootViewController: ))import SwiftUI import GoogleMobileAds struct ContentView: View { @State var interstitialAdObject: GADInterstitialAd? = nil let prensentationAnchorView: RepresentedEmptyViewController = .init() func loadAd() async { do { self.interstitialAdObject = try await GADInterstitialAd.load(withAdUnitID: "ユニットID", request: GADRequest()) } catch { print(error) } } func showInterstitialAd() { self.interstitialAdObject.present(fromRootViewController: prensentationAnchorView.viewController) } var body: some View { VStack { Button(action: showInterstitialAd) { Text("広告を表示") } } .task { await loadAd() } .background { prensentationAnchorView } } }
こうすることで、Button 押下時にインタースティシャル広告が表示されるようになります。
presentationAnchorView を起点として、モーダルが表示されるイメージですね。