it-swarm.com.de

iPhone - So finden Sie den Controller für die oberste Ansicht

Ich bin jetzt auf einige Fälle gestoßen, in denen es praktisch wäre, den "obersten" View-Controller (den für die aktuelle Ansicht verantwortlichen) zu finden, aber keinen Weg gefunden, dies zu tun.

Grundsätzlich besteht die Herausforderung darin, dass manin einer Klasse ausführt, die kein View-Controller(oder eine View)ist und keine Adresse einer aktiven View hat.]und die Adresse des obersten Ansichtscontrollers (oder die Adresse des Navigationscontrollers) wurde noch nicht übergeben. Ist es möglich, diesen Viewcontroller zu finden? (Und wenn ja, wie?)

Oder ist es möglich, die oberste Ansicht zu finden?

230
Hot Licks

in iOS 4 wurde die rootViewController-Eigenschaft für UIWindow eingeführt:

[UIApplication sharedApplication].keyWindow.rootViewController;

Sie müssen es jedoch selbst festlegen, nachdem Sie den View Controller erstellt haben.

77

Ich denke, Sie brauchen eine Kombination aus der akzeptierten Antwort und @ fishstix's

+ (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
}
397
Eric

Um JonasGs answer zu vervollständigen (der die Registerkarten-Controller beim Durchfahren weggelassen hat), ist hier meine Version des Zurücksenden des derzeit sichtbaren Ansichtscontrollers:

- (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;
    }
}
148
kleo

Eine vollständige, nicht rekursive Version, die verschiedene Szenarien berücksichtigt:

  • Der View Controller zeigt eine andere Ansicht
  • Der View Controller ist eine UINavigationController
  • Der View Controller ist eine UITabBarController

Ziel 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;
     }
 }

Swift 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
    }
}
40
Yuchen Zhong

Erste Ansichtssteuerung für Swift mithilfe von Erweiterungen

Code:  

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()
    }
}

Verwendungszweck:

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

Um Eric's answer zu vervollständigen (wer Popovers, Navigationssteuerungen, Registerkartensteuerungen, Ansichtssteuerungen, die als Unteransichten zu anderen Ansichtssteuerungen hinzugefügt wurden, während des Durchlaufens ausließ), möchte ich hier die derzeit sichtbare Ansichtssteuerung zurückgeben:

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

- (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;
    }
}

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

Jetzt müssen Sie nur noch die obige Methode aufrufen, um die meisten View-Controller zu erhalten:

UIViewController *topMostViewControllerObj = [self topViewController];
24
Rajesh

Diese Antwort enthält childViewControllers und behält eine saubere und lesbare Implementierung bei.

+ (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;
}
19
Awesome-o

Ich habe vor kurzem diese Situation in einem meiner Projekte erhalten, bei dem eine Benachrichtigungsansicht angezeigt wurde, unabhängig davon, welcher Controller angezeigt wurde und welcher Typ (UINavigationController, klassischer Controller oder benutzerdefinierter View-Controller), wenn sich der Netzwerkstatus geändert hat.

Also habe ich meinen Code veröffentlicht, der ziemlich einfach ist und eigentlich auf einem Protokoll basiert, so dass er mit jeder Art von Containercontroller flexibel ist .. __ er scheint mit den letzten Antworten verwandt zu sein, aber auf eine sehr flexible Art und Weise.

Sie können den Code hier herunterladen: PPTopMostController

Und bekam den obersten Controller 

UIViewController *c = [UIViewController topMostController];
13
ipodishima

Dies ist eine Verbesserung der Antwort von Eric:

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) ist eine Hilfsfunktion.

Jetzt müssen Sie nur noch topMostController() aufrufen und der oberste UIViewController sollte zurückgegeben werden!

11
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;
    }
}
8
lifuqing_ios

Einfache Erweiterung für UIApplication in Swift:

HINWEIS:

Es kümmert sich um moreNavigationController innerhalb von 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
    }
}

Einfache Verwendung:

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

Hier ist meine Meinung dazu. Vielen Dank an @Stakenborg für den Weg, UIAlertView als Top-Controller zu verwenden

-(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;
}
7
Kamran Khan
 @ Implementierung UIWindow (Erweiterungen) 

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

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

 return topController; 
} 

 @ end 
7
FishStix

Für die neueste Swift Version:
Erstellen Sie eine Datei, benennen Sie sie UIWindowExtension.Swift und fügen Sie den folgenden Ausschnitt ein:

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
}

Verwenden Sie es überall als:

if let topVC = getTopViewController() {

}
7
Ashok Kumar S

Hier ist was für mich gearbeitet hat. 

Ich habe festgestellt, dass der Controller manchmal im Schlüsselfenster null war, da das Schlüsselfenster eine OS-Sache wie eine Warnung usw. ist. 

 + (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
Tom Andersen

Alternative Swift-Lösung:

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

    return topController!
}
3
Esqarrouth

Wenn Sie die Antwort von @ Eric erweitern, müssen Sie darauf achten, dass das keyWindow tatsächlich das gewünschte Fenster ist. Wenn Sie versuchen, diese Methode zu verwenden, nachdem Sie beispielsweise in einer Warnmeldungsansicht auf etwas getippt haben, wird keyWindow tatsächlich das Fenster der Warnmeldung sein. Dies passierte mir in der Wildnis beim Umgang mit Deep Links über einen Alarm und verursachte SIGABRTs mit NO STACK TRACE. Total Hündin zu debuggen.

Hier ist der Code, den ich jetzt verwende:

- (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;
}

Fühlen Sie sich frei, dies mit dem gewünschten Geschmack zu kombinieren, bei dem Sie den Top-View-Controller aus den anderen Antworten auf diese Frage abrufen.

3
Stakenborg

Diese Lösung ist die vollständigste. Es berücksichtigt Folgendes: UINavigationController UIPageViewController UITabBarController Und der oben dargestellte View-Controller aus dem Draufsicht-Controller

Das Beispiel ist in Swift 3.

Es gibt 3 Überlastungen

//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
Marc Renaud

Noch eine andere schnelle Lösung

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)

}
3
Martin Algesten

Ich denke, die meisten Antworten haben UINavigationViewController vollständig ignoriert, also habe ich diesen Anwendungsfall mit der folgenden Implementierung behandelt.

+ (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;
}
2
Aamir

Große Lösung in Swift, in AppDelegate implementieren

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
}
2
Edward Novelo

Und noch eine schnelle Lösung

extension UIViewController {
    static var topmostViewController: UIViewController? {
        return UIApplication.sharedApplication().keyWindow?.topmostViewController
    }

    var topmostViewController: UIViewController? {
        return presentedViewController?.topmostViewController ?? self
    }
}

extension UINavigationController {
    override var topmostViewController: UIViewController? {
        return visibleViewController?.topmostViewController
    }
}

extension UITabBarController {
    override var topmostViewController: UIViewController? {
        return selectedViewController?.topmostViewController
    }
}

extension UIWindow {
    var topmostViewController: UIViewController? {
        return rootViewController?.topmostViewController
    }
}
1
bzz

Im Folgenden finden Sie zwei Funktionen, mit denen Sie den topViewController auf Stack of View Controllern finden können. Möglicherweise müssen Sie später Anpassungen vornehmen, aber für diesen Code ist das Verständnis des Konzepts von topViewController oder des Stapels von viewControllers hervorragend.

- (UIViewController*)findTopViewController {

  id  topControler  = [self topMostController];

  UIViewController* topViewController;
  if([topControler isKindOfClass:[UINavigationController class]]) {
        topViewController = [[(UINavigationController*)topControler viewControllers] lastObject];
   } else if ([topControler isKindOfClass:[UITabBarController class]]) {
        //Here you can get reference of top viewcontroller from stack of viewcontrollers on UITabBarController
  } else {
        //topController is a preented viewController
        topViewController = (UIViewController*)topControler;
  }
    //NSLog(@"Top ViewController is: %@",NSStringFromClass([topController class]));
    return topViewController;
}

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

    while (topController.presentedViewController) {
        topController = topController.presentedViewController;
    }
    //NSLog(@"Top View is: %@",NSStringFromClass([topController class]));
    return topController;
}

Sie können die Methode [viewController-Klasse] verwenden, um den Klassentyp eines viewControllers herauszufinden.

1
iDevAmit

Schnell:

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;
    }
}
}

Verwendungszweck:

 if let topController = window.visibleViewController() {
            println(topController)
        }
1
Bobj-C

Viele dieser Antworten sind unvollständig. Obwohl dies in Objective-C ist, ist dies die beste Zusammenstellung von allen, für die ich mich jetzt als nicht-rekursiver Block zusammenstellen könnte:

UIViewController *(^topmostViewControllerForFrontmostNormalLevelWindow)(void) = ^UIViewController *{
    // NOTE: Adapted from various stray answers here:
    //   https://stackoverflow.com/questions/6131205/iphone-how-to-find-topmost-view-controller/20515681

    UIViewController *viewController;

    for (UIWindow *window in UIApplication.sharedApplication.windows.reverseObjectEnumerator.allObjects) {
        if (window.windowLevel == UIWindowLevelNormal) {
            viewController = window.rootViewController;
            break;
        }
    }

    while (viewController != nil) {
        if ([viewController isKindOfClass:[UITabBarController class]]) {
            viewController = ((UITabBarController *)viewController).selectedViewController;
        } else if ([viewController isKindOfClass:[UINavigationController class]]) {
            viewController = ((UINavigationController *)viewController).visibleViewController;
        } else if (viewController.presentedViewController != nil && !viewController.presentedViewController.isBeingDismissed) {
            viewController = viewController.presentedViewController;
        } else if (viewController.childViewControllers.count > 0) {
            viewController = viewController.childViewControllers.lastObject;
        } else {
            BOOL repeat = NO;

            for (UIView *view in viewController.view.subviews.reverseObjectEnumerator.allObjects) {
                if ([view.nextResponder isKindOfClass:[UIViewController class]]) {
                    viewController = (UIViewController *)view.nextResponder;

                    repeat = YES;
                    break;
                }
            }

            if (!repeat) {
                break;
            }
        }
    }

    return viewController;
};
1
Ben Guild

Verwenden Sie die folgende Erweiterung, um die aktuelle sichtbare UIViewController zu erfassen. Arbeitete für Swift 4.0 und höher

Code:

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
    }
}

Wie benutzt man?

let objViewcontroller = UIApplication.topViewController()
1
iMHitesh Surani

Ich bin mir nicht sicher, ob dies beim Ermitteln des obersten View-Controllers hilfreich ist, aber ich habe versucht, einen neuen View-Controller zu präsentieren. Wenn mein Root-View-Controller jedoch bereits ein modales Dialogfeld enthält, wird er blockiert würde mit diesem Code an den Anfang aller modalen Ansichts-Controller wechseln:

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

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

Um viel Komplexität zu vermeiden, verfolge ich den aktuellen viewController, indem ich im Delegaten einen viewController erstellt und ihn in jede viewDidLoad-Methode einfügt. Wenn Sie also eine neue Ansicht laden, entspricht der ViewController, der sich im Delegaten befindet, dem viewController dieser Ansicht . Das mag hässlich sein, aber es funktioniert wunderbar, und es ist nicht nötig, einen Navigationscontroller oder irgendeinen anderen Unsinn zu haben.

0
J_Tuck

Swift 4.2 Erweiterung


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
    }
}

Verwenden Sie es von überall aus wie

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

oder wie

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

Passt zu allen Klassen wie UINavigationController, UITabBarController

Genießen!

0
Saranjith

Wenn es sich bei dem Root-Controller um einen Navigations-Controller handelt, können Sie den obersten sichtbaren Controller folgendermaßen finden:

UIViewController *rootVC = [[UIApplication sharedApplication] keyWindow].rootViewController;
if ([rootVC respondsToSelector:@selector(visibleViewController)])
{
    UIViewController *topVC = [(UINavigationController *)rootVC visibleViewController];
    // do your thing with topVC
}

Hier ist ein Auszug aus UINavigationController.h:

@property(nonatomic,readonly,retain) UIViewController *topViewController; // The top view controller on the stack.
@property(nonatomic,readonly,retain) UIViewController *visibleViewController; // Return modal view controller if it exists. Otherwise the top view controller.
0
amit

Dies funktioniert hervorragend, wenn Sie den Controller für die Draufsicht 1 von einem beliebigen Root-View-Controller finden

+ (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
johnnyg17

Mein Problem war etwas anders. Ich benutzte SWRevealViewController in meiner Anwendung ... Ich verwendete Yuchen Zhongs answer , aber es wird immer topViewController als SWRevealViewController zurückgegeben. Für diejenigen, die SWRevealViewController oder ein anderes Pod verwenden, um sideMenu zu entwickeln. Hier ist meine Erweiterung zu Yuchen Zhongs Antwort:

extension UIApplication {
class func topViewController() -> UIViewController? {
    var topVC = shared.keyWindow!.rootViewController
    while true {
        if let presented = topVC?.presentedViewController {
            topVC = presented
        } else if let nav = topVC as? UINavigationController {
            topVC = nav.visibleViewController
        } else if let tab = topVC as? UITabBarController {
            topVC = tab.selectedViewController
        }else if let swRVC = topVC as? SWRevealViewController {
            topVC = swRVC.frontViewController
        } else {
            break
        }
    }
    return topVC
}
}
0
Gulfam Khan

Die vorherige Antwort scheint keine Fälle zu behandeln, in denen rootController UITabBarController oder UINavigationController ist.

Hier ist die Funktion in Swift, die für diese Fälle funktioniert:

func getCurrentView() -> UIViewController?
{
    if let window = UIApplication.sharedApplication().keyWindow, var currentView: UIViewController = window.rootViewController
    {
        while (currentView.presentedViewController != nil)
        {
            if let presented = currentView.presentedViewController
            {
                currentView = presented
            }
        }

        if currentView is UITabBarController
        {
            if let visible = (currentView as! UITabBarController).selectedViewController
            {
                currentView = visible;
            }
        }

        if currentView is UINavigationController
        {
            if let visible = (currentView as! UINavigationController).visibleViewController
            {
                currentView = visible;
            }
        }

        return currentView
    }

    return nil
}
0
Dragouf

Ich denke, dass hier vielleicht etwas übersehen wird. Vielleicht ist es besser, den übergeordneten viewController in die Funktion zu übergeben, die den viewController verwendet. Wenn Sie in der Ansichtshierarchie nach dem Top-View-Controller suchen, verstößt er möglicherweise gegen die Trennung von Model-Layer und UI-Layer und ist ein Codegeruch. Wenn ich nur darauf hinwies, tat ich das Gleiche, dann wurde mir klar, dass es viel einfacher war, die Funktion zu übergeben, indem die Modelloperation zur UI-Schicht zurückkehrte, wo ich einen Bezug zum View-Controller habe. 

0
possen

sie können den obersten View-Controller finden, indem Sie verwenden

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

Hier ist eine schnelle Implementierung einer App mit UINavigationController als Root. 

  if let nav = UIApplication.sharedApplication().keyWindow?.rootViewController as? UINavigationController{
        //get the current's navigation view controller
        var vc = nav.topViewController
        while vc?.presentedViewController != nil {
            vc = vc?.presentedViewController
        }
        return vc
    }
0

Ich denke, die Lösung von Rajesh ist nahezu perfekt, aber ich denke, es ist besser, Unteransichten von oben nach unten zu durchqueren. Ich habe folgende Änderungen vorgenommen:

+ (UIViewController *)topViewController:(UIViewController *)viewController{

    if (viewController.presentedViewController)
    {

            UIViewController *presentedViewController = viewController.presentedViewController;
            return [self topViewController:presentedViewController];
     } 
     else if ([viewController isKindOfClass:[UITabBarController class]])
     {

            UITabBarController *tabBarController = (UITabBarController *)viewController;
            return [self topViewController:tabBarController.selectedViewController];
    }

         else if ([viewController isKindOfClass:[UINavigationController class]])
    {   

            UINavigationController *navController = (UINavigationController *)viewController;

            return [self topViewController:navController.visibleViewController];
    }

    // Handling UIViewController's added as subviews to some other views.
    else {

        NSInteger subCount = [viewController.view subviews].count - 1;

        for (NSInteger index = subCount; index >=0 ; --index)
        {

            UIView *view = [[viewController.view subviews] objectAtIndex:index];

            id subViewController = [view nextResponder];    // Key property which most of us are unaware of / rarely use.

            if ( subViewController && [subViewController isKindOfClass:[UIViewController class]])
            {
                return [self topViewController:subViewController];
            }
        }
        return viewController;
    }
}
0
Siguang Zhang

Eine andere Lösung basiert auf der Responder-Kette, die je nach dem, was der erste Responder ist, funktionieren kann oder nicht:

  1. Holen Sie sich den ersten Responder .
  2. Holen Sie sich den UIViewController, der diesem ersten Responder zugeordnet ist .

Beispiel-Pseudo-Code:

+ (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
Senseful

Eine prägnante, aber umfassende Lösung in Swift 4.2 berücksichtigt UINavigationControllers, UITabBarControllers, Present und child View-Controller:

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()
  }
}

Verwendungszweck:

let viewController = UIApplication.shared.topmostViewController()
0
NAlexN