概要
実行時に 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
を起点として、モーダルが表示されるイメージですね。