概要
SwiftUIのプロジェクトで、現在フロントにあるUIViewControllerを手軽に取得する方法についてのTips
SwiftUIとUIKitの連携
SwiftUIのViewとUIKit間での連携は
- UIHostingController
- UIViewRepresentable
を使った方法がある。
ただ、SwiftUIのプロジェクトで外部ライブラリなど使用する場合に
いま提示している「UIViewController」を渡すことが必要になるケースがある。
例えば、Googleの「UserMessagingPlatform」を使って
ATT事前許諾プロンプトを提示する際に
SwiftUIのコンテキスト上からライブラリ側にUIViewControllerを渡す必要がある。
方法は他にも色々ありそうだが、その時の対応メモ。
現在のUIViewControllerを探す
表示中のUIViewControllerを探す流れは
まず
- UIApplicationから接続中のUISceneたちを取得
- 前面にあるアクティブなSceneをフィルタ
- SceneからUIWindowSceneを探す
- UIWindowSceneが持つUIWindowからKeyWindowを探す
- Windowから rootViewController or そこから生えているpresentedViewControllerを見つける
なお、ここで実際に取得できるのは
UIHostingController<SomeOneView>
となり
これを任意のライブラリなりに渡せば良い。
検証
// 1
let scenes = UIApplication.shared.connectedScenes
debugPrint(scenes)
// 2
let active_scenes = scenes.filter({$0.activationState == .foregroundActive})
debugPrint(active_scenes)
// 3
let window_scenes = active_scenes.map({$0 as? UIWindowScene})
debugPrint(window_scenes)
// 4
let windows = window_scenes.compactMap({$0}).first?.windows
debugPrint(windows)
let first_keywindow = windows?.filter({$0.isKeyWindow}).first
debugPrint(first_keywindow)
// 5
let vc = first_keywindow?.rootViewController
if let _vc = vc?.presentedViewController {
debugPrint(String(describing: type(of: vc!)))
}else{
debugPrint(String(describing: type(of: _vc)))
}
// UIHostingController<ContextView>
上をまとめたもの
例えば、UIViewControllerにExtensionで追加して
extension UIViewController {
static func getFrontViewController() -> UIViewController? {
let keyWindow = UIApplication.shared.connectedScenes
.filter({$0.activationState == .foregroundActive})
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows
.filter({$0.isKeyWindow}).first
let vc = keyWindow?.rootViewController
guard let _vc = vc?.presentedViewController else {
return vc
}
return _vc
}
}
guard let vc = UIViewController.getFrontViewController() else {
return
}
// vc
みたいに使用したり。