Swiftで最前面のViewControllerを取得する方法10選 – Japanシーモア

Swiftで最前面のViewControllerを取得する方法10選

SwiftのアイコンとハイライトされたViewControllerのイメージSwift
この記事は約26分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

このサービスは複数のSSPによる協力の下、運営されています。

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

Swiftを学び始めた方、あるいはすでに慣れてきた方にとって、特定の情報や操作の取得方法は常に関心の高いトピックの一つです。

特に、アプリ開発の中心となるViewControllerに関連した情報の取得は、より高度なアプリケーションの開発やデバッグをスムーズに進めるための重要なスキルと言えるでしょう。

この記事を読めば、Swiftで最前面のViewControllerを簡単に取得する方法が身につくようになります。

●SwiftとViewControllerの基本

SwiftはAppleが開発したプログラミング言語で、iOS, macOS, watchOS, tvOSなどのAppleのプラットフォームで動作するアプリケーションの開発に広く使用されています。

その魅力として、シンプルな文法や高いパフォーマンス、そして安全性が挙げられます。

○Swiftの概要と特徴

Swiftは、Objective-Cに代わる新しい言語として2014年に発表されました。

その後のバージョンアップを経て、さまざまな強力な機能や安全性の向上が図られています。

  • シンプルな文法:Swiftは読みやすく、書きやすい文法を持っており、初心者にも学びやすいです。
  • 高速実行:既存のObjective-Cコードと組み合わせて使用することもでき、その実行速度は非常に高速です。
  • 安全性:Swiftは、エラーを事前に防ぐための設計がなされており、実行時のエラーが大幅に削減されます。

○ViewControllerの役割と特性

ViewControllerは、iOSアプリケーションの画面一つ一つの背後にあるロジックや動作を制御する役割を持っています。

具体的には、ユーザーの入力に応じてデータの更新を行ったり、新しい画面への遷移を制御するなど、アプリケーションの動作の中核を担っています。

  • ライフサイクル:ViewControllerは、画面が表示されたときや非表示になったときなど、さまざまなライフサイクルのイベントに応じてメソッドが呼ばれます。これにより、適切なタイミングでの処理が可能となります。
  • Viewとの連携:ViewControllerは、それに紐づくViewとの間でデータの受け渡しや更新を行います。この連携により、ユーザーインターフェースとロジックが一体となった動作を実現しています。
  • 再利用性:同じロジックを持つViewControllerを複数の場所で再利用することができます。これにより、開発の効率化やコードの整理が進められます。

Swiftの魅力的な特性と、ViewControllerの中心的な役割を理解したところで、次に、最前面のViewControllerを取得する方法について詳しく見ていきましょう。

この後の内容は、さらに実践的な内容となり、具体的なサンプルコードと共に解説を進めていきます。

●最前面のViewControllerの取得方法

iOSアプリ開発の中で、現在表示されている最前面のViewControllerを正確に取得することは、多くのシナリオで役立ちます。

特に、アラートの表示、データの受け渡し、または特定の状態に応じたUIの変更など、さまざまなタスクを効率的に実行するためには欠かせない技術です。

ここでは、Swiftで最前面のViewControllerを取得するための10の方法を紹介します。

○サンプルコード1:UIWindowを使用する方法

この方法は、アプリケーションのメインウィンドウを取得し、そのウィンドウのrootViewControllerプロパティを使用して最前面のViewControllerを取得するものです。

if let rootViewController = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.rootViewController {
    print(rootViewController)
}

このコードでは、UIApplication.shared.windowsを使ってアプリケーション内のすべてのウィンドウを取得し、その中からキーウィンドウとなっているものを検索しています。そして、そのキーウィンドウのrootViewControllerを取得しています。

この方法はシンプルであり、ほとんどのシナリオでうまく動作しますが、複数のウィンドウや画面が存在する場合には、期待するViewControllerが取得できない場合もあります。

○サンプルコード2:Responder Chainを利用する方法

Responder Chainは、イベントの処理や配信に関連するオブジェクトの階層的な連鎖です。

このチェーンを利用して、最前面のViewControllerを効率的に取得することができます。

extension UIApplication {
    var topMostViewController: UIViewController? {
        return keyWindow?.rootViewController?.topMostViewController
    }
}

extension UIViewController {
    var topMostViewController: UIViewController {
        if let presentedViewController = presentedViewController {
            return presentedViewController.topMostViewController
        }
        return self
    }
}

このコードでは、UIApplicationUIViewControllerに拡張を追加しています。

UIApplicationの拡張には、topMostViewControllerという計算型プロパティが定義され、keyWindowrootViewControllerを起点として、階層的に最上部のViewControllerを検索するためのtopMostViewControllerを呼び出しています。

○サンプルコード3:現在のNavigation Stackを調べる方法

Navigation ControllerはiOSアプリケーションの中で、画面遷移を管理する主要なツールの一つです。

このNavigation Controllerは、ViewControllerのスタックを内部的に持っており、このスタックを調べることで、現在アクティブなViewControllerを確認することが可能です。

具体的には、UINavigationControllerクラスのviewControllersプロパティを使用して、Navigation Controllerが持つViewControllerのスタックを取得します。

このスタックの最後の要素が、最前面に表示されているViewControllerになります。

if let navigationController = UIApplication.shared.keyWindow?.rootViewController as? UINavigationController {
    let topViewController = navigationController.viewControllers.last
    print(topViewController)
}

このコードは、アプリケーションのキーウィンドウのrootViewControllerUINavigationControllerである場合、そのNavigation Controllerのスタック内の最後のViewControllerを取得しています。

この方法を使用する際には、rootViewControllerが必ずUINavigationControllerであるとは限らないため、キャストの成否を確認する必要があります。

また、TabBarControllerやその他のContainer View Controllerを使用している場合、更に詳細な調査が必要となります。

○サンプルコード4:Modal ViewControllerの確認方法

モーダル表示されるViewControllerは、アプリの通常の画面遷移フローの上に重ねられる形で表示されるため、最前面のViewControllerとして取得したい場合が多いです。

モーダル表示されているViewControllerを取得するには、現在のViewControllerのpresentedViewControllerプロパティを使用します。

if let presentedViewController = UIApplication.shared.keyWindow?.rootViewController?.presentedViewController {
    print(presentedViewController)
}

このコードは、キーウィンドウのrootViewControllerがモーダル表示しているViewControllerがある場合、そのViewControllerを取得しています。

しかし、複数のモーダルが連鎖的に表示されている場合、最前面のViewControllerを取得するために、繰り返しpresentedViewControllerを確認するロジックが必要になります。

このロジックは再帰的な関数やループを利用して実装することができます。

○サンプルコード5:Extensionsを使用した取得方法

Swiftでは、既存のクラスや構造体に新しい機能を追加するための方法として、Extension(拡張)が提供されています。

Extensionを利用することで、既存の型に新しいメソッドやプロパティを追加することができ、これを使用して最前面のViewControllerを取得するカスタムメソッドを追加することも可能です。

ここでは、UIApplicationクラスにExtensionを追加し、最前面のViewControllerを取得するカスタムメソッドを実装します。

extension UIApplication {
    func topViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let nav = base as? UINavigationController {
            return topViewController(base: nav.visibleViewController)
        }
        if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
            return topViewController(base: selected)
        }
        if let presented = base?.presentedViewController {
            return topViewController(base: presented)
        }
        return base
    }
}

このコードでは、topViewControllerというカスタムメソッドをUIApplicationに追加しています。

このメソッドは再帰的に動作し、最前面のViewControllerを取得するロジックを含んでいます。

例えば、アプリ内でこのメソッドを呼び出す場合、次のように使用します。

if let viewController = UIApplication.shared.topViewController() {
    print(viewController)
}

このコードを実行すると、最前面にあるViewControllerのインスタンスが取得され、コンソールにその情報が出力されます。

○サンプルコード6:Key Windowを活用する方法

iOSアプリケーションにおける表示領域は、Windowとして管理されています。

このWindowの中で、最も前面に表示されているものがKey Windowと呼ばれます。

UIApplicationkeyWindowプロパティを使用することで、Key Windowを取得し、その上に表示されているViewControllerを確認することができます。

if let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
    print(rootViewController)
}

このコードでは、Key WindowのrootViewControllerプロパティを取得しています。

通常、アプリケーションが起動された時に最初に表示されるViewControllerが、このrootViewControllerとして設定されます。

ただし、NavigationControllerやTabBarControllerが使用されている場合、このrootViewControllerがそのControllerになるので、さらにその上のViewControllerを確認する必要があります。

○サンプルコード7:TopViewControllerのカスタム関数

Swiftでアプリを開発する際、最前面のViewControllerを取得するニーズは頻繁にあります。

ここでは、カスタム関数を使用して、アプリケーション内で簡単に最前面のViewControllerを取得する方法をご紹介します。

下記のカスタム関数は、UIViewControllerを拡張し、topMostViewControllerという新しいプロパティを追加します。

このプロパティを使うことで、どのViewControllerからでも現在の最前面のViewControllerを取得することができます。

extension UIViewController {
    var topMostViewController: UIViewController {
        if let presented = self.presentedViewController {
            return presented.topMostViewController
        }

        if let navigation = self as? UINavigationController {
            return navigation.visibleViewController?.topMostViewController ?? self
        }

        if let tab = self as? UITabBarController {
            return tab.selectedViewController?.topMostViewController ?? self
        }

        return self
    }
}

このコードでは、UIViewControllertopMostViewControllerというプロパティを追加しています。

このプロパティは再帰的に最前面のViewControllerを探索します。

アプリ内でこのプロパティを使用する方法は次のとおりです。

let topViewController = UIViewController().topMostViewController
print(topViewController)

上記のコードを実行すると、現在最前面に表示されているViewControllerのインスタンスが取得され、その情報がコンソールに出力されます。

○サンプルコード8:第三者ライブラリを活用する方法

Swiftのコミュニティは非常に活発で、多くのライブラリが提供されています。

最前面のViewControllerを簡単に取得するための第三者ライブラリもいくつか存在します。

例として、TopViewControllerFinderという架空のライブラリを考えます。

このライブラリを使用することで、簡単に最前面のViewControllerを取得することができます。

ライブラリをプロジェクトに追加した後、次のように使用します。

import TopViewControllerFinder

let topVC = TopViewControllerFinder.shared.find()
print(topVC)

上記のコードを実行すると、ライブラリが提供するfindメソッドを使用して、最前面にあるViewControllerのインスタンスを取得できます。

○サンプルコード9:関連するSubViewの調査

アプリケーションのデバッグやカスタマイズの際、最前面のViewControllerに関連するSubViewを詳細に調査することが必要になることがあります。

SwiftでこのようなSubViewの調査を行う方法を学びましょう。

ViewControllerには多くのSubViewが関連付けられていることが多いです。

これらのSubViewを取得してリスト化することで、それぞれのSubViewの属性や状態を確認することができます。

ここでは、最前面のViewControllerに関連する全てのSubViewをリスト化するサンプルコードです。

func listSubviews(of view: UIView, indent: String = "") {
    print("\(indent)\(view)")
    for subview in view.subviews {
        listSubviews(of: subview, indent: indent + "  ")
    }
}

if let topViewController = UIApplication.shared.keyWindow?.rootViewController {
    listSubviews(of: topViewController.view)
}

このコードでは、listSubviewsという関数を定義しています。

この関数は、指定されたUIViewとそのSubViewを再帰的にリスト化してコンソールに出力します。

最後の部分で、最前面のViewControllerのViewとそれに関連するSubViewを出力するようにしています。

このコードを実行すると、最前面のViewControllerに関連するSubViewの階層がコンソールに表示されます。

これにより、どのようなSubViewが配置されているのか、またそれぞれのSubViewの関連性や配置の仕方を確認することができます。

○サンプルコード10:通知を利用して取得する方法

アプリケーションの動作中、特定のイベントが発生した際に最前面のViewControllerを取得することも考えられます。

このような場合、NotificationCenterを使用して通知を受け取り、そのタイミングで最前面のViewControllerを取得する方法が有効です。

ここでは、特定の通知を受け取った際に最前面のViewControllerを取得するサンプルコードを紹介します。

NotificationCenter.default.addObserver(forName: NSNotification.Name("SomeNotificationName"), object: nil, queue: .main) { notification in
    if let topViewController = UIApplication.shared.keyWindow?.rootViewController {
        print(topViewController)
    }
}

このコードでは、NotificationCenterを使用して、SomeNotificationNameという名前の通知を監視しています。

この通知を受け取った際の処理として、最前面のViewControllerを取得してコンソールに出力しています。

この方法を使用することで、特定のイベントやアクションが発生した際の最前面のViewControllerの状態を取得し、その情報を利用してさまざまな処理を実装することができます。

●SwiftでのViewControllerの応用例

SwiftでViewControllerを使った開発を行う際、単に表示を変更するだけではなく、様々な応用的な方法が考えられます。

ここでは、そのような応用例としてのサンプルコードとその詳細な説明を3つ紹介します。

○サンプルコード1:最前面のViewControllerからデータを取得する

アプリケーション開発時、最前面のViewControllerからデータを取得し、他の場所で利用したい場面があります。

下記のサンプルコードは、最前面のViewControllerから特定のデータを取得する方法を表しています。

if let topViewController = UIApplication.shared.keyWindow?.rootViewController as? DataContainingViewController {
    let data = topViewController.data
    print(data)
}

このコードでは、DataContainingViewControllerという名前のViewControllerが最前面に表示されている場合、そのViewControllerが持っているdataという変数の内容を取得し、コンソールに出力しています。

○サンプルコード2:最前面のViewControllerの変更を検出する

アプリケーションの状態やユーザーのアクションに応じて表示しているViewControllerが変わる場合、その変更を検出して何らかの処理を行いたいことがあります。

下記のサンプルコードは、ViewControllerの表示が変わるたびに特定の処理を行う方法を表しています。

class CustomViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("ViewControllerが表示されました。")
    }
}

このコードでは、CustomViewControllerというViewControllerが表示されるたびに、viewDidAppearメソッドが呼び出され、コンソールにメッセージが出力されます。

○サンプルコード3:アニメーションやトランジションのカスタマイズ

ViewController間の遷移時にカスタムアニメーションやトランジションを追加することで、ユーザーエクスペリエンスを向上させることができます。

下記のサンプルコードは、カスタムトランジションを適用して、新しいViewControllerをフェードインする方法を表しています。

class FadeTransition: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toViewController = transitionContext.viewController(forKey: .to) else { return }
        transitionContext.containerView.addSubview(toViewController.view)
        toViewController.view.alpha = 0
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
            toViewController.view.alpha = 1
        }) { _ in
            transitionContext.completeTransition(true)
        }
    }
}

このコードでは、FadeTransitionというカスタムトランジションクラスを定義しています。

遷移先のViewControllerの透明度を0から1に変化させることで、フェードインのアニメーションを実現しています。

●注意点と対処法

Swiftでの開発において、最前面のViewControllerを取得する際に注意すべきポイントやその対処法を深く理解することは、効率的なコーディングとアプリの品質向上に繋がります。

○Threadの注意点

Swiftでは、UIの操作は主スレッドでのみ行う必要があります。

しかし、非同期処理を行った際に意識せずにUI操作を行うと、アプリがクラッシュするリスクが高まります。

例として、非同期処理後に最前面のViewControllerを取得するコードを考えます。

DispatchQueue.global().async {
    // 何らかの非同期処理
    if let topViewController = UIApplication.shared.keyWindow?.rootViewController {
        print(topViewController)
    }
}

このコードを実行すると、非同期処理の中でUI操作を行っているため、不安定な動作が発生する可能性があります。

対処法として、非同期処理後のUI操作は主スレッドで行うようにしましょう。

DispatchQueue.global().async {
    // 何らかの非同期処理
    DispatchQueue.main.async {
        if let topViewController = UIApplication.shared.keyWindow?.rootViewController {
            print(topViewController)
        }
    }
}

このように、DispatchQueue.main.asyncを使用して、非同期処理後のUI操作を主スレッドで行うことが推奨されます。

○Memory Leakのリスクと対策

ViewControllerの取得や操作中に、強参照サイクルが発生してしまうとメモリリークが発生することがあります。

これは、オブジェクト間での参照が循環してしまい、ガーベジコレクションが行えなくなる現象を指します。

例えば、ViewController内でクロージャを使用する際に自身を参照してしまうと、この問題が発生する可能性があります。

class MyViewController: UIViewController {
    var completionHandler: (() -> Void)?

    func setup() {
        completionHandler = {
            // 何らかの処理
            self.view.backgroundColor = .red
        }
    }
}

このコードでは、completionHandlerクロージャ内でselfを直接参照しています。

このため、強参照サイクルが発生しメモリリークが起こる可能性があります。

対処法として、クロージャ内でのself参照は弱参照[weak self]または非保持参照[unowned self]を使用することで、強参照サイクルを避けることができます。

class MyViewController: UIViewController {
    var completionHandler: (() -> Void)?

    func setup() {
        completionHandler = { [weak self] in
            // 何らかの処理
            self?.view.backgroundColor = .red
        }
    }
}

○最前面のViewControllerの取得頻度の調整

最前面のViewControllerを頻繁に取得すると、パフォーマンスに影響が出る可能性があります。

特に大規模なアプリや複雑な画面遷移が行われる場合、取得の頻度を無駄に高めてしまうと、アプリの動作が遅くなるリスクが考えられます。

対処法としては、必要なタイミングでのみ最前面のViewControllerを取得するように心掛けることが重要です。

また、取得したViewControllerのインスタンスをキャッシュして再利用することで、取得の頻度を抑えることもできます。

●カスタマイズ方法

Swiftでの開発において、最前面のViewControllerを取得する方法は多岐にわたりますが、プロジェクトの要件や状況に応じてカスタマイズする必要が生じることも少なくありません。

ここでは、より柔軟に取得を行うためのカスタマイズ方法を2つの観点から詳しく紹介します。

○カスタムExtensionの作成

SwiftにおけるExtensionは、既存のクラスや構造体に新しい機能を追加する強力なツールです。

この特性を利用して、UIViewControllerに対してカスタムな取得方法を追加することができます。

例として、最前面のViewControllerを返すExtensionを作成します。

extension UIViewController {
    static var topMostViewController: UIViewController? {
        var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
        while let presentedController = topController?.presentedViewController {
            topController = presentedController
        }
        return topController
    }
}

このコードでは、UIViewControllerのExtensionとしてtopMostViewControllerプロパティを追加しています。

このプロパティを使用することで、簡単に最前面のViewControllerを取得することができます。

○第三者ライブラリのカスタマイズ方法

Swiftのコミュニティには、ViewControllerの操作を助けるための多くのライブラリが存在します。

これらのライブラリを利用することで、高度な操作や独自のカスタマイズが可能となります。

例として、ある人気ライブラリを使用して、最前面のViewControllerに特定の操作を追加する方法を考えます。

まず、ライブラリをプロジェクトに導入します。

// 依存管理ツールを利用してライブラリをインストール
// 例: CocoaPodsの場合
// pod 'YourLibraryName'

次に、該当のライブラリを利用して最前面のViewControllerを取得し、特定の操作を追加します。

import YourLibraryName

class CustomViewControllerOperation {
    func customizeTopViewController() {
        if let topVC = YourLibraryName.getTopViewController() {
            // ここで特定のカスタマイズを行う
            topVC.view.backgroundColor = .blue
        }
    }
}

このコードは、YourLibraryNameというライブラリを利用して、最前面のViewControllerの背景色を青に変更するカスタム操作を追加しています。

まとめ

Swiftでの開発において、最前面のViewControllerを取得する方法は非常に重要です。

本記事では、その取得方法の基本から、応用例、注意点、さらにはカスタマイズ方法まで幅広く詳細に解説しました。

これらの方法を駆使することで、さまざまなシチュエーションに柔軟に対応し、開発の質や効率を向上させることができます。

特に、カスタムExtensionの作成や第三者ライブラリの活用は、Swiftの強力な拡張性を最大限に生かすアプローチとなります。

これらを適切に組み合わせることで、あなたのアプリ開発がさらにスムーズに進むことでしょう。

最後に、開発においては常に最新の情報を取得し、実践することが重要です。

Swiftや関連技術のアップデートに目を光らせ、新しい知識を継続的に取り入れることで、より高度なアプリケーション開発が可能となります。

これからもSwiftを中心とした開発に挑戦し続けるすべての方々に、成功と成果が訪れることを心から願っています。