Help us understand the problem. What is going on with this article?

AppDelegateから一番上のUIViewControllerにアクセスする方法

AppDelegateからUI操作を行う時、UIViewControllerが複数重なっている時は一番上(最前面)のUIViewControllerにアクセスする必要がありますよね。
今回はその方法について調べてみました。

準備

以下の様な3つのUIViewControllerをSegueで順に起動するサンプルプログラムを作成します。
AppDelegate.png
青いUIViewControllerにはFirstViewController、黄色いUIViewControllerにはSecondViewController、赤いUIViewControllerにはThirdViewControllerというクラス名をつけておきます。

処理

今回はAppDelegateのapplicationWillEnterForegroundに処理を記述しました。
ホームボタンやホームインジケータを押下してアプリを一度バックグラウンドにして、その後フォアグラウンドに復帰させた時に走るイベントです。

AppDelegate.swift
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を取得して、そこからアラートを表示する処理を記述しています。

サンプルプログラムを動かすとフォアグラウンドになった時に現在表示している画面をアラートで表示します。
ezgif-4-4554282cb2d2.gif

解説

まず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です"
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした