AppDelegateからUI操作を行う時、UIViewControllerが複数重なっている時は一番上(最前面)のUIViewControllerにアクセスする必要がありますよね。
今回はその方法について調べてみました。
準備
以下の様な3つのUIViewControllerをSegueで順に起動するサンプルプログラムを作成します。
青いUIViewControllerにはFirstViewController、黄色いUIViewControllerにはSecondViewController、赤いUIViewControllerにはThirdViewControllerというクラス名をつけておきます。
処理
今回はAppDelegateのapplicationWillEnterForegroundに処理を記述しました。
ホームボタンやホームインジケータを押下してアプリを一度バックグラウンドにして、その後フォアグラウンドに復帰させた時に走るイベントです。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
-------------------- (中略) --------------------
func applicationWillEnterForeground(_ application: UIApplication) {
//一番手前のViewControllerを取得
var viewController = UIApplication.shared.keyWindow?.rootViewController
//PresentedViewControllerが取得できなくなるまでループ
while viewController!.presentedViewController != nil {
let presentedViewController = viewController!.presentedViewController
//UIAlertControllerは対象外とする
if let alertController = presentedViewController as? UIAlertController {
//一番上がUIAlertControllerであればUIAlertControllerを閉じる
alertController.dismiss(animated: true, completion: nil)
break
} else {
//UIAlertControllerでなければ取得
viewController = presentedViewController
}
}
var message: String = ""
if viewController is FirstViewController {
message = "1番目のViewControllerです"
}
if viewController is SecondViewController {
message = "2番目のViewControllerです"
}
if viewController is ThirdViewController {
message = "3番目のViewControllerです"
}
let alertController: UIAlertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
viewController!.present(alertController, animated: true, completion: nil)
}
}
一番上のUIViewControllerを取得して、そこからアラートを表示する処理を記述しています。
サンプルプログラムを動かすとフォアグラウンドになった時に現在表示している画面をアラートで表示します。
解説
まずUIApplicationからrootViewController(一番手前のUIViewController)を取得します。
var viewController = UIApplication.shared.keyWindow?.rootViewController
そしてrootViewControllerからpresentedViewControllerをループして取得します。
while viewController!.presentedViewController != nil {
let presentedViewController = viewController!.presentedViewController
一番最後に返ってきたpresentedViewControllerが一番上のUIViewControllerになるのですが、ここで1点注意が必要です。
アラートを表示するUIAlertControllerや、App Extensionsを表示するUIActivityViewControllerもUIViewControllerを継承しているので、presentedViewControllerで返ってきます。
その為、一番上がUIAlertControllerやUIActiveViewControllerであった場合はその一つ手前のUIViewControllerが一番上のUIViewControllerになります。
今回のサンプルでもUIAlertControllerが表示されている可能性はあるので、UIAlertControllerだったらそれを閉じて、その一つ手前のUIViewControllerを一番上のUIViewControllerとして取得するようにしています。
while viewController!.presentedViewController != nil {
let presentedViewController = viewController!.presentedViewController
//UIAlertControllerは対象外とする
if let alertController = presentedViewController as? UIAlertController {
//一番上がUIAlertControllerであればUIAlertControllerを閉じる
alertController.dismiss(animated: true, completion: nil)
break
} else {
//UIAlertControllerでなければ取得
viewController = presentedViewController
}
}
どのUIViewControllerが一番上に来ているのかは「is」を使用してクラス名と型比較をすればわかります。
var message: String = ""
if viewController is FirstViewController {
message = "1番目のViewControllerです"
}
if viewController is SecondViewController {
message = "2番目のViewControllerです"
}
if viewController is ThirdViewController {
message = "3番目のViewControllerです"
}