iOSで最上位のView Controllerを見つける方法


253

「最上位の」ビューコントローラー(現在のビューを担当するビューコントローラー)を見つけることができると便利な場合がいくつかありますが、それを行う方法が見つかりません。

基本的に課題はこれです:ビューコントローラー(またはビュー)ではない[アクティブなビューのアドレスを持たない] クラスで実行されており、最上位のビューコントローラーのアドレスが渡されていない(または、たとえば、ナビゲーションコントローラのアドレス)、そのビューコントローラを見つけることは可能ですか?(そして、もしそうなら、どうですか?)

それとも失敗すると、最上位のビューを見つけることは可能ですか?


つまり、それは不可能だと言っているのです。
ホットリックス

@ダニエルいいえ、あなたはこれを知る必要はほとんどないはずなので、あなたのコードはいくつかの再設計を使用できるようだと言っています。また、「最上位」の考え方は特定の状況でのみ有効であり、常にそうであるとは限りません。
Dave DeLong、

@ダニエル私はあなたの質問を誤解していた。ifsとbutsはこれに答えようとしています。これは、ビューコントローラーのフローによって異なります。@ウィルバーの答えは、それを追跡するための良い出発点になるはずです。
Deepak Danduprolu、2011年

さて、特定のケースに単純化しましょう。UIAlertViewのクローンを作成したい場合、どうすればよいですか?他のコントローラーやビューにアドレス指定を渡さなくても、正常に機能することに注意してください。
ホットリックス

4
@Daniel:2つ目のUIWindowを追加すると、アラートビューのようなオーバーレイでうまく機能します。
Wilbur Vandrsmith、2011年

回答:


75

iOS 4では、UIWindowにrootViewControllerプロパティが導入されました。

[UIApplication sharedApplication].keyWindow.rootViewController;

ただし、View Controllerを作成した後で、自分で設定する必要があります。


155
ウィルバー、これはあなたがオペレーションが要求したものの反対をあなたに与えるでしょう。rootViewControllerは、一番上というよりむしろベースビューコントローラです。
m4rkk 2012年

3
m4rkk:「最上位」は、どの方向から見ているかによって異なります。新しいコントローラーは上部(スタックのような)または下部(ツリーのような)に追加されますか?いずれにせよ、OPはナビゲーションコントローラーが上にあると説明しました。
Wilbur Vandrsmith、2012年

50
単語「top」はビューコントローラに使用されます。つまり、上部に視覚的に表示されます(など-[UINavigationController topViewController])。そして、ある単語「根」があり、ツリーのルート(のような-[UIWindow rootViewController]
Tricertops

13
@ImpurestClub私はドキュメントで見つけることができません、Xcodeはそれを見つけていないようです。
Drux

4
@adibいいえ、UINavigationControllerに属しています
David H

428

受け入れられた回答と@fishstixの組み合わせが必要だと思います

+ (UIViewController*) topMostController
{
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    return topController;
}

Swift 3.0以降

func topMostController() -> UIViewController? {
    guard let window = UIApplication.shared.keyWindow, let rootViewController = window.rootViewController else {
        return nil
    }

    var topController = rootViewController

    while let newTopController = topController.presentedViewController {
        topController = newTopController
    }

    return topController
}

4
さらに、を確認しUINavigationControllerて要求することtopViewControllerも、確認しUITabBarControllerて要求することもできselectedViewControllerます。これにより、現在ユーザーに表示されているビューコントローラーが表示されます。
Tricertops 2013年

33
これは完全に解決されたソリューションではありません。これは、(UINavigationController、UITabBarControllerなどで使用される)childViewControllersの階層ではなく、モーダル表示されたView Controllerの階層のみを横断するためです。
藻類

3
これは、現在のアプリケーションの状態に戻るモーダルビューコントローラーの表示を抽象化するのに最適な方法です。私の場合は、アプリケーションがタイムアウトした後のパスワード再入力画面でした。ありがとう!
erversteeg 2013年

11
@algal:本当に:UITabBarController、UINavigationController は、すでに階層の最上位のビューコントローラーです。「最上位のコントローラー」で何をしたいかによっては、それらをまったく走査せずに、その内容をいじる必要がない場合があります。私の場合、すべての上にモーダルコントローラーを表示することでした。そのためには、コンテンツはなく、UINaviationControllerまたはUITabBarControllerを取得する必要があります
Rick77 2014年

1
@ Rick77、これが真実である場合、ここに埋め込まれたあなたの1つの小さなコメントは、他の回答での複雑な変更のトンを不必要にします。他の誰もこれについてまったく言及していないので、それが真実であることを確認するようにお願いする必要があると私は感じています。そしてもしそうなら、それはそれ自体がすべて答えになるに値するほど重要です。他の回答の大多数は、この問題に対処しようとするバク転をしているためです。あなたは命を救うでしょう!
Le Motは2015年

150

JonasGの回答(トラバース中にタブバーコントローラーを省略)を完了するために、現在表示されているビューコントローラーを返す私のバージョンを次に示します。

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}

2
いいね、ええ、私は
TabBar

9
含まないchildViewControllers
Awesome-o

以下の私の回答を見てください。ポップオーバー、トラバース中に他のビューコントローラーにサブビューとして追加されたビューコントローラーなどの@kleoが除外したケースを処理することで、上記の回答を改善します
Rajesh

return [self topViewControllerWithRootViewController:navigationController.visibleViewController];を使用している場合、それがUIAlertControllerであっても、visibleViewController自体が提示されたビューコントローラ(IF ANY)を返します。UIアラートコントローラーを回避する必要がある人は、visibleViewControllerの代わりにtopViewControllerを使用してください
Johnykutty

これに50セントを追加するだけです。webViewをロードするビューコントローラーでこれを機能させるのに苦労していました。これが機能しなかった理由は、ビューがまだ準備されていない(ロードが完了していない)ためです。そのため、表示されませんでした。UINavigationControllerが可視のViewControllerを取得しようとしているときに、まだ可視のViewControllerがないため、topViewContollerの取得が失敗する状況につながりました。したがって、この問題が発生した場合は、上記のtopViewControllerメソッドを呼び出す前に、ビューの読み込みが完了していることを確認してください。
mbuster 2016年

52

さまざまなシナリオを処理する完全な非再帰バージョン:

  • ビューコントローラは別のビューを提示しています
  • ビューコントローラーは UINavigationController
  • ビューコントローラーは UITabBarController

Objective-C

 UIViewController *topViewController = self.window.rootViewController;
 while (true)
 {
     if (topViewController.presentedViewController) {
         topViewController = topViewController.presentedViewController;
     } else if ([topViewController isKindOfClass:[UINavigationController class]]) {
         UINavigationController *nav = (UINavigationController *)topViewController;
         topViewController = nav.topViewController;
     } else if ([topViewController isKindOfClass:[UITabBarController class]]) {
         UITabBarController *tab = (UITabBarController *)topViewController;
         topViewController = tab.selectedViewController;
     } else {
         break;
     }
 }

スウィフト4+

extension UIWindow {
    func topViewController() -> UIViewController? {
        var top = self.rootViewController
        while true {
            if let presented = top?.presentedViewController {
                top = presented
            } else if let nav = top as? UINavigationController {
                top = nav.visibleViewController
            } else if let tab = top as? UITabBarController {
                top = tab.selectedViewController
            } else {
                break
            }
        }
        return top
    }
}

2
visibleViewControllerわかりやすくするために名前を付けました。
ジョニー

31

拡張機能を使用してSwiftの最上位のView Controllerを取得する

コード:

extension UIViewController {
    @objc func topMostViewController() -> UIViewController {
        // Handling Modal views
        if let presentedViewController = self.presentedViewController {
            return presentedViewController.topMostViewController()
        }
        // Handling UIViewController's added as subviews to some other views.
        else {
            for view in self.view.subviews
            {
                // Key property which most of us are unaware of / rarely use.
                if let subViewController = view.next {
                    if subViewController is UIViewController {
                        let viewController = subViewController as! UIViewController
                        return viewController.topMostViewController()
                    }
                }
            }
            return self
        }
    }
}

extension UITabBarController {
    override func topMostViewController() -> UIViewController {
        return self.selectedViewController!.topMostViewController()
    }
}

extension UINavigationController {
    override func topMostViewController() -> UIViewController {
        return self.visibleViewController!.topMostViewController()
    }
}

使用法:

UIApplication.sharedApplication().keyWindow!.rootViewController!.topMostViewController()

すばらしい-この解決策を本当にありがとう。subviews'trickが必要でした!繰り返しますが、ありがとう、あなたは私の日を救った。
iKK

25

Ericの答え(ポップオーバー、ナビゲーションコントローラー、tabbarcontrollers、トラバース中に他のビューコントローラーにサブビューとして追加されたビューコントローラー)を完了するために、現在表示されているビューコントローラーを返す私のバージョンを次に示します。

================================================== ===================

- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)viewController {
    if ([viewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)viewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([viewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navContObj = (UINavigationController*)viewController;
        return [self topViewControllerWithRootViewController:navContObj.visibleViewController];
    } else if (viewController.presentedViewController && !viewController.presentedViewController.isBeingDismissed) {
        UIViewController* presentedViewController = viewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    }
    else {
        for (UIView *view in [viewController.view subviews])
        {
            id subViewController = [view nextResponder];
            if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
            {
                if ([(UIViewController *)subViewController presentedViewController]  && ![subViewController presentedViewController].isBeingDismissed) {
                    return [self topViewControllerWithRootViewController:[(UIViewController *)subViewController presentedViewController]];
                }
            }
        }
        return viewController;
    }
}

================================================== ===================

そして今、あなたが一番上のビューコントローラを取得するために必要なことは、次のように上記のメソッドを呼び出すことです:

UIViewController *topMostViewControllerObj = [self topViewController];

SplitViewControllerもありませんか?
apinho 2017年

21

この回答はchildViewControllers、クリーンで読みやすい実装を含み、維持しています。

+ (UIViewController *)topViewController
{
    UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;

    return [rootViewController topVisibleViewController];
}

- (UIViewController *)topVisibleViewController
{
    if ([self isKindOfClass:[UITabBarController class]])
    {
        UITabBarController *tabBarController = (UITabBarController *)self;
        return [tabBarController.selectedViewController topVisibleViewController];
    }
    else if ([self isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *navigationController = (UINavigationController *)self;
        return [navigationController.visibleViewController topVisibleViewController];
    }
    else if (self.presentedViewController)
    {
        return [self.presentedViewController topVisibleViewController];
    }
    else if (self.childViewControllers.count > 0)
    {
        return [self.childViewControllers.lastObject topVisibleViewController];
    }

    return self;
}

一部のコードを更新しました。最小化して再度復元することで、それがどのコントローラーであるかを示します。nik-kov-ios-developer.blogspot.ru/2016/12/...
ニクKOV

ねえ、さあ、あなたの "topVisibleViewController"はどこにあるの?
Paradise

12

私は最近、この状況に遭遇しました。このプロジェクトでは、ネットワークステータスが変化したときに、表示されたコントローラーやタイプ(UINavigationController、クラシックコントローラー、カスタムビューコントローラー)を問わず、通知ビューを表示する必要がありました。

だから私は自分のコードをリリースしました。これは非常に簡単で、実際にはプロトコルに基づいているので、あらゆる種類のコンテナーコントローラーに柔軟に対応できます。それは最後の答えと関連しているようですが、はるかに柔軟な方法で。

ここでコードを取得できます:PPTopMostController

そして、最高のコントローラーを使用して

UIViewController *c = [UIViewController topMostController];

10

これはエリックの答えの改善です:

UIViewController *_topMostController(UIViewController *cont) {
    UIViewController *topController = cont;

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }

    if ([topController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visible = ((UINavigationController *)topController).visibleViewController;
        if (visible) {
            topController = visible;
        }
    }

    return (topController != cont ? topController : nil);
}

UIViewController *topMostController() {
    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;

    UIViewController *next = nil;

    while ((next = _topMostController(topController)) != nil) {
        topController = next;
    }

    return topController;
}

_topMostController(UIViewController *cont) ヘルパー関数です。

これで、必要なのは呼び出しでtopMostController()あり、一番上のUIViewControllerが返されます。


7
1983年以来私は言うでしょう。Objective-CにはCが含まれていることを覚えておいてください。C関数でObjCコードをラップすることは一般的な慣行です。そのため、これはObjective-Cコードです。
JonasG

@JonasGこんにちは、Jonas、CでObjCコードをラップするのはどのような場合に適していますか?なぜなら、私はこのようなC関数を時々見かけ、使用法を区別できないからです。Cでコードをラップすると、パフォーマンス上の利点がありますか?
OzBoz 2015年

1
@OzBoz self所属するクラスがすぐに明確でない状況。
adib

8

これが私の考えです。UIAlertViewを最上位のコントローラーとして取得するのをスキップする方法を指摘してくれた@Stakenborgに感謝

-(UIWindow *) returnWindowWithWindowLevelNormal
{
    NSArray *windows = [UIApplication sharedApplication].windows;
    for(UIWindow *topWindow in windows)
    {
        if (topWindow.windowLevel == UIWindowLevelNormal)
            return topWindow;
    }
    return [UIApplication sharedApplication].keyWindow;
}

-(UIViewController *) getTopMostController
{
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal)
    {
        topWindow = [self returnWindowWithWindowLevelNormal];
    }

    UIViewController *topController = topWindow.rootViewController;
    if(topController == nil)
    {
        topWindow = [UIApplication sharedApplication].delegate.window;
        if (topWindow.windowLevel != UIWindowLevelNormal)
        {
            topWindow = [self returnWindowWithWindowLevelNormal];
        }
        topController = topWindow.rootViewController;
    }

    while(topController.presentedViewController)
    {
        topController = topController.presentedViewController;
    }

    if([topController isKindOfClass:[UINavigationController class]])
    {
        UINavigationController *nav = (UINavigationController*)topController;
        topController = [nav.viewControllers lastObject];

        while(topController.presentedViewController)
        {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}

getSomething:Objective-Cのようにメソッドに名前を付けることは避けてください。これには特別な意味があり(詳細:cocoadevcentral.com/articles/000082.php)、コードでこれらの要件を満たしていません。
2015

7
@implementation UIWindow(拡張)

-(UIViewController *)topMostController
{
    UIViewController * topController = [self rootViewController];

    while(topController.presentedViewController){
        topController = topController.presentedViewController;
    }

    topControllerを返します。
}

@終わり

元の投稿に記載されている条件を満たしていないと思います。
ホットリックス

7
- (UIViewController*)topViewController {
    return [self topViewControllerWithRootViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}

- (UIViewController*)topViewControllerWithRootViewController:(UIViewController*)rootViewController {
    if ([rootViewController isKindOfClass:[UITabBarController class]]) {
        UITabBarController* tabBarController = (UITabBarController*)rootViewController;
        return [self topViewControllerWithRootViewController:tabBarController.selectedViewController];
    } else if ([rootViewController isKindOfClass:[UINavigationController class]]) {
        UINavigationController* navigationController = (UINavigationController*)rootViewController;
        return [self topViewControllerWithRootViewController:navigationController.visibleViewController];
    } else if (rootViewController.presentedViewController) {
        UIViewController* presentedViewController = rootViewController.presentedViewController;
        return [self topViewControllerWithRootViewController:presentedViewController];
    } else {
        return rootViewController;
    }
}

私はこれを使用しましたが、表示されたビューコントローラーが複数あると壊れるので注意してください
Chuck Boris

7

最新のSwiftバージョンの場合:
ファイルを作成し、名前UIWindowExtension.swiftを付けて、次のスニペットを貼り付けます。

import UIKit

public extension UIWindow {
    public var visibleViewController: UIViewController? {
        return UIWindow.getVisibleViewControllerFrom(self.rootViewController)
    }

    public static func getVisibleViewControllerFrom(vc: UIViewController?) -> UIViewController? {
        if let nc = vc as? UINavigationController {
            return UIWindow.getVisibleViewControllerFrom(nc.visibleViewController)
        } else if let tc = vc as? UITabBarController {
            return UIWindow.getVisibleViewControllerFrom(tc.selectedViewController)
        } else {
            if let pvc = vc?.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(pvc)
            } else {
                return vc
            }
        }
    }
}

func getTopViewController() -> UIViewController? {
    let appDelegate = UIApplication.sharedApplication().delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}

次のようにどこでも使用できます。

if let topVC = getTopViewController() {

}

私はあなたの答えをあまり変えたくありませんが、いくつかのことを提案します。1. UISplitViewControllerのサポートを追加します。2. switchif if elseの代わりに使用します。3.静的関数も必要かどうかわからない。宣言した最初のインスタンスレベルの変数でこれを簡単に実行できると思います。4.多分グローバル関数を作りすぎないことが最善ですが、それは好みの問題です。あなたは、グローバル関数の効果を達成するために、1行のコードを使用することができますUIApplication.sharedApplication().delegate?.window?.visibleViewController
ジョーダン・スミス

7

UIApplicationSwiftでの単純な拡張:

注意:

それはmoreNavigationController内に気にUITabBarController

extension UIApplication {

    class func topViewController(baseViewController: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

        if let navigationController = baseViewController as? UINavigationController {
            return topViewController(navigationController.visibleViewController)
        }

        if let tabBarViewController = baseViewController as? UITabBarController {

            let moreNavigationController = tabBarViewController.moreNavigationController

            if let topViewController = moreNavigationController.topViewController where topViewController.view.window != nil {
                return topViewController(topViewController)
            } else if let selectedViewController = tabBarViewController.selectedViewController {
                return topViewController(selectedViewController)
            }
        }

        if let splitViewController = baseViewController as? UISplitViewController where splitViewController.viewControllers.count == 1 {
            return topViewController(splitViewController.viewControllers[0])
        }

        if let presentedViewController = baseViewController?.presentedViewController {
            return topViewController(presentedViewController)
        }

        return baseViewController
    }
}

簡単な使い方:

if let topViewController = UIApplication.topViewController() {
    //do sth with top view controller
}

YES YES YES-topMostViewControllerを見つけるためのWebの周りに多くの解決策がありますが、もしあなたのアプリがMoreタブを持つタブバーを持っているなら、あなたはそれを少し違った方法で扱わなければなりません。
アンディObusek 2018

7

以下の拡張機能を使用して、現在表示されているものを取得しますUIViewController。Swift 4.0以降で動作

Swift 4.0以降:

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

使い方?

let objViewcontroller = UIApplication.topViewController()

とケースのpresentedViewController前に、このテストを最初に行うべきではありませんか?それ以外の場合、ビューコントローラーがUINavigationControllerUITabBarControllerUINavigationControllerまたはUITabBarController表示されているビューコントローラーであっても、トップビューコントローラーとして返されません。
ドリュー

4

さらに別のSwiftソリューション

func topController() -> UIViewController? {

    // recursive follow
    func follow(from:UIViewController?) -> UIViewController? {
        if let to = (from as? UITabBarController)?.selectedViewController {
            return follow(to)
        } else if let to = (from as? UINavigationController)?.visibleViewController {
            return follow(to)
        } else if let to = from?.presentedViewController {
            return follow(to)
        }
        return from
    }

    let root = UIApplication.sharedApplication().keyWindow?.rootViewController

    return follow(root)

}

4

Swift 4.2拡張


extension UIApplication {

    class func topViewController(controller: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
        if let navigationController = controller as? UINavigationController {
            return topViewController(controller: navigationController.visibleViewController)
        }
        if let tabController = controller as? UITabBarController {
            if let selected = tabController.selectedViewController {
                return topViewController(controller: selected)
            }
        }
        if let presented = controller?.presentedViewController {


            return topViewController(controller: presented)
        }
        return controller
    }
}

どこからでも使用できます

 UIApplication.topViewController()?.present(yourController, animated: true, completion: nil)

など

 UIApplication.topViewController()?
                    .navigationController?
                    .popToViewController(yourController,
                                         animated: true)

UINavigationController、UITabBarControllerなどのクラスに適合

楽しい!


1
@BilalBakhromは、「賛成です。あなたの答えが最善だと思います。直接topViewController()メソッドを呼び出すことはできません。UIApplicationクラスはシングルトンです。「共有」と呼ばれるインスタンスを使用してください。」却下するために投票した編集で。これが実際に正しい場合は、ここをクリックして、その編集を承認できるメニューに移動してください。
wizzwizz4

3

ここに私のために働いたものがあります。

keyWindowはアラートなどのOSのものであるため、コントローラーがキーウィンドウでnilになることがありました。

 + (UIViewController*)topMostController
 {
     UIWindow *topWndow = [UIApplication sharedApplication].keyWindow;
     UIViewController *topController = topWndow.rootViewController;

     if (topController == nil)
     {
         // The windows in the array are ordered from back to front by window level; thus,
         // the last window in the array is on top of all other app windows.
         for (UIWindow *aWndow in [[UIApplication sharedApplication].windows reverseObjectEnumerator])
         {
             topController = aWndow.rootViewController;
             if (topController)
                 break;
         }
     }

     while (topController.presentedViewController) {
         topController = topController.presentedViewController;
     }

     return topController;
 }

3

@Ericの答えを拡張すると、keyWindowが実際に必要なウィンドウになることに注意する必要があります。たとえば、アラートビューで何かをタップした後でこのメソッドを利用しようとすると、keyWindowは実際にはアラートのウィンドウになり、間違いなく問題が発生します。これは、アラートを介してディープリンクを処理しているときに実際に発生し、スタックトレースのないSIGABRTを引き起こしました。デバッグする雌犬。

これが私が今使っているコードです:

- (UIViewController *)getTopMostViewController {
    UIWindow *topWindow = [UIApplication sharedApplication].keyWindow;
    if (topWindow.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [UIApplication sharedApplication].windows;
        for(topWindow in windows)
        {
            if (topWindow.windowLevel == UIWindowLevelNormal)
                break;
        }
    }

    UIViewController *topViewController = topWindow.rootViewController;

    while (topViewController.presentedViewController) {
        topViewController = topViewController.presentedViewController;
    }

    return topViewController;
}

これを、この質問の他の回答から好きなトップビューコントローラーを取得するフレーバーと自由に組み合わせてください。


これは完全なソリューションであると思いましたか?他の回答の多くは非常に複雑で、非常に多くのエッジケースを説明しようとしています。私これが本当であって欲しい、それはとてもシンプルでエレガントです。
Le Motは2015年

私はそれについて問題を経験したことがありません。ナビゲーションスタックで異常なことをしていなければ、これは機能するはずです。そうでなければ、他のソリューションのいくつかは、より複雑なケースを処理します。
Stakenborg、2015年

3

代替のSwiftソリューション:

static func topMostController() -> UIViewController {
    var topController = UIApplication.sharedApplication().keyWindow?.rootViewController
    while (topController?.presentedViewController != nil) {
        topController = topController?.presentedViewController
    }

    return topController!
}

3

このソリューションが最も完全です。それは考慮に入れます:UINavigationController UIPageViewController UITabBarControllerそしてトップビューコントローラーから表示された最上位のビューコントローラー

例はSwift 3にあります。

3つのオーバーロードがあります

//Get the topmost view controller for the current application.
public func MGGetTopMostViewController() -> UIViewController?  {

    if let currentWindow:UIWindow = UIApplication.shared.keyWindow {
        return MGGetTopMostViewController(fromWindow: currentWindow)
    }

    return nil
}

//Gets the topmost view controller from a specific window.
public func MGGetTopMostViewController(fromWindow window:UIWindow) -> UIViewController? {

    if let rootViewController:UIViewController = window.rootViewController
    {
        return MGGetTopMostViewController(fromViewController:  rootViewController)
    }

    return nil
}


//Gets the topmost view controller starting from a specific UIViewController
//Pass the rootViewController into this to get the apps top most view controller
public func MGGetTopMostViewController(fromViewController viewController:UIViewController) -> UIViewController {

    //UINavigationController
    if let navigationViewController:UINavigationController = viewController as? UINavigationController {
        let viewControllers:[UIViewController] = navigationViewController.viewControllers
        if navigationViewController.viewControllers.count >= 1 {
            return MGGetTopMostViewController(fromViewController: viewControllers[viewControllers.count - 1])
        }
    }

    //UIPageViewController
    if let pageViewController:UIPageViewController = viewController as? UIPageViewController {
        if let viewControllers:[UIViewController] = pageViewController.viewControllers {
            if viewControllers.count >= 1 {
                return MGGetTopMostViewController(fromViewController: viewControllers[0])
            }
        }
    }

    //UITabViewController
    if let tabBarController:UITabBarController = viewController as? UITabBarController {
        if let selectedViewController:UIViewController = tabBarController.selectedViewController {
            return MGGetTopMostViewController(fromViewController: selectedViewController)
        }
    }

    //Lastly, Attempt to get the topmost presented view controller
    var presentedViewController:UIViewController! = viewController.presentedViewController
    var nextPresentedViewController:UIViewController! = presentedViewController?.presentedViewController

    //If there is a presented view controller, get the top most prensentedViewController and return it.
    if presentedViewController != nil {
        while nextPresentedViewController != nil {

            //Set the presented view controller as the next one.
            presentedViewController = nextPresentedViewController

            //Attempt to get the next presented view controller
            nextPresentedViewController = presentedViewController.presentedViewController
        }
        return presentedViewController
    }

    //If there is no topmost presented view controller, return the view controller itself.
    return viewController
}

3

Swift 4.2の簡潔で包括的なソリューションは、UINavigationControllersUITabBarControllers提示されたビューコントローラーを考慮します。

extension UIViewController {
  func topmostViewController() -> UIViewController {
    if let navigationVC = self as? UINavigationController,
      let topVC = navigationVC.topViewController {
      return topVC.topmostViewController()
    }
    if let tabBarVC = self as? UITabBarController,
      let selectedVC = tabBarVC.selectedViewController {
      return selectedVC.topmostViewController()
    }
    if let presentedVC = presentedViewController {
      return presentedVC.topmostViewController()
    }
    if let childVC = children.last {
      return childVC.topmostViewController()
    }
    return self
  }
}

extension UIApplication {
  func topmostViewController() -> UIViewController? {
    return keyWindow?.rootViewController?.topmostViewController()
  }
}

使用法:

let viewController = UIApplication.shared.topmostViewController()

2

Swiftでの優れたソリューション、AppDelegateでの実装

func getTopViewController()->UIViewController{
    return topViewControllerWithRootViewController(UIApplication.sharedApplication().keyWindow!.rootViewController!)
}
func topViewControllerWithRootViewController(rootViewController:UIViewController)->UIViewController{
    if rootViewController is UITabBarController{
        let tabBarController = rootViewController as! UITabBarController
        return topViewControllerWithRootViewController(tabBarController.selectedViewController!)
    }
    if rootViewController is UINavigationController{
        let navBarController = rootViewController as! UINavigationController
        return topViewControllerWithRootViewController(navBarController.visibleViewController)
    }
    if let presentedViewController = rootViewController.presentedViewController {
        return topViewControllerWithRootViewController(presentedViewController)
    }
    return rootViewController
}

1

ほとんどの回答は完全に無視されていると思うUINavigationViewControllerので、次の実装でこのユースケースを処理しました。

+ (UIViewController *)topMostController {
    UIViewController * topController = [UIApplication sharedApplication].keyWindow.rootViewController;
    while (topController.presentedViewController || [topController isMemberOfClass:[UINavigationController class]]) {
        if([topController isMemberOfClass:[UINavigationController class]]) {
            topController = [topController childViewControllers].lastObject;
        } else {
            topController = topController.presentedViewController;
        }
    }

    return topController;
}

1

私はそれが非常に遅いことを知っており、冗長かもしれません。しかし、私が思いついたスニペットは次のとおりです:

    static func topViewController() -> UIViewController? {
        return topViewController(vc: UIApplication.shared.keyWindow?.rootViewController)
    }

    private static func topViewController(vc:UIViewController?) -> UIViewController? {
        if let rootVC = vc {
            guard let presentedVC = rootVC.presentedViewController else {
                return rootVC
            }
            if let presentedNavVC = presentedVC as? UINavigationController {
                let lastVC = presentedNavVC.viewControllers.last
                return topViewController(vc: lastVC)
            }
            return topViewController(vc: presentedVC)
        }
        return nil
    }

0

これは、ルートビューコントロールからトップビューコントローラ1を見つけるのに最適です。

+ (UIViewController *)topViewControllerFor:(UIViewController *)viewController
{
    if(!viewController.presentedViewController)
        return viewController;
    return [MF5AppDelegate topViewControllerFor:viewController.presentedViewController];
}

/* View Controller for Visible View */

AppDelegate *app = [UIApplication sharedApplication].delegate;
UIViewController *visibleViewController = [AppDelegate topViewControllerFor:app.window.rootViewController]; 

0

これが最上位のビューコントローラーを見つけることで達成しようとしていることの助けになるかどうかはわかりませんが、新しいビューコントローラーを提示しようとしていましたが、ルートビューコントローラーにモーダルダイアログが既にある場合はブロックされるため、このコードを使用して、すべてのモーダルビューコントローラーの一番上に循環します。

UIViewController* parentController =[UIApplication sharedApplication].keyWindow.rootViewController;

while( parentController.presentedViewController &&
       parentController != parentController.presentedViewController )
{
    parentController = parentController.presentedViewController;
}

0

あなたは使用することで一番上のビューコントローラーを見つけることができます

NSArray *arrViewControllers=[[self navigationController] viewControllers];
UIViewController *topMostViewController=(UIViewController *)[arrViewControllers objectAtIndex:[arrViewControllers count]-1];

それを除いて、あなたが実際に質問を読んだ場合selfnavigationControllerプロパティはありません。
Hot Licks 14

0

別の解決策は、最初のレスポンダーが何であるかによって機能する場合と機能しない場合があるレスポンダーチェーンに依存します。

  1. 最初のレスポンダーを取得します。
  2. その最初のレスポンダに関連付けられたUIViewControllerを取得します。

擬似コードの例:

+ (UIViewController *)currentViewController {
    UIView *firstResponder = [self firstResponder]; // from the first link above, but not guaranteed to return a UIView, so this should be handled more appropriately.
    UIViewController *viewController = [firstResponder viewController]; // from the second link above
    return viewController;
}

0

迅速:

extension UIWindow {

func visibleViewController() -> UIViewController? {
    if let rootViewController: UIViewController  = self.rootViewController {
        return UIWindow.getVisibleViewControllerFrom(rootViewController)
    }
    return nil
}

class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
if vc.isKindOfClass(UINavigationController.self) {

    let navigationController = vc as UINavigationController
    return UIWindow.getVisibleViewControllerFrom( navigationController.visibleViewController)

} else if vc.isKindOfClass(UITabBarController.self) {

    let tabBarController = vc as UITabBarController
    return UIWindow.getVisibleViewControllerFrom(tabBarController.selectedViewController!)

} else {

    if let presentedViewController = vc.presentedViewController {

        return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)

    } else {

        return vc;
    }
}
}

使用法:

 if let topController = window.visibleViewController() {
            println(topController)
        }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.