İOS'ta en üstteki görüntü denetleyicisini bulma


253

Şimdi "en üst" görünüm denetleyicisi (geçerli görünümden sorumlu olanı) bulmak için uygun olurdu birkaç durumda karşılaştım, ama bunu yapmak için bir yol bulamadık.

Temel olarak zorluk şudur: Kişi, bir görünüm denetleyicisi (veya görünüm) olmayan [ve etkin bir görünümün adresine sahip olmayan ] bir sınıfta yürütülüyor ve en üst görünüm denetleyicisinin ( veya gezinme denetleyicisinin adresi), bu görünüm denetleyicisini bulmak mümkün müdür? (Ve eğer öyleyse, nasıl?)

Ya da başarısız olursa, en üstteki görünümü bulmak mümkün mü?


Yani bunun mümkün olmadığını söylüyorsun.
Hot Licks

@ Daniel hayır, söylüyorum, kodunuzun bazı yeniden tasarımları kullanabileceği anlaşılıyor, çünkü bunu nadiren bilmeniz gerekir. Ayrıca, "en üstteki" fikri yalnızca belirli bağlamlarda geçerlidir ve o zaman bile her zaman geçerli değildir.
Dave DeLong

@ Daniel Sorunu yanlış okudum. Buna cevap vermeye çalışan birçok ifs ve buts var. Bu, görünüm denetleyicisi akışınıza bağlıdır. @ Wilbur'un cevabı, izini sürmek için iyi bir başlangıç ​​noktası olmalıdır.
Deepak Danduprolu

Peki, belirli bir duruma basitleştirelim. Bir UIAlertView klonu yazmak isteseydim, bunu nasıl yaparım? Diğer denetleyicilere veya görünümlere herhangi bir adreslenebilirlik aktarılmadan düzgün çalışabileceğini unutmayın.
Hot Licks

4
@Daniel: İkinci bir UIWindow eklemek, uyarı görünümü benzeri kaplamalar için iyi çalışır.
Wilbur Vandrsmith

Yanıtlar:


75

iOS 4, UIWindow'da rootViewController özelliğini tanıttı:

[UIApplication sharedApplication].keyWindow.rootViewController;

Yine de görünüm denetleyicisini oluşturduktan sonra kendiniz ayarlamanız gerekir.


155
Wilbur, bu size op'un istediđinin tam tersini verecek. rootViewController en üstteki yerine temel görünüm denetleyicisidir.
m4rkk

3
m4rkk: "En üstteki" hangi yöne baktığınıza bağlıdır. Yeni denetleyiciler üste (yığın benzeri) veya alta (ağaç benzeri) eklenir mi? Her durumda, OP navigasyon kontrol cihazının üstte olduğunu belirtti ve bu da aşağıya doğru görünümü gösteriyor.
Wilbur Vandrsmith

50
“Top” kelimesi üstte görsel olarak bulunan (örneğin -[UINavigationController topViewController]) görünüm denetleyicisi için kullanılır . Sonra ağacın kökü olan “kök” kelimesi vardır (örneğin -[UIWindow rootViewController].
Tricertops

13
@ImpurestClub Belgelerde bulamıyorum, Xcode bulamıyor gibi görünüyor.
Drux

4
hayır @adib, ait olduğu UINavigationController
David H

428

Bence kabul edilen cevap ve @ fishstix

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

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

    return topController;
}

Swift 3.0 ve üzeri

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
Buna ek olarak, kontrol edebilir UINavigationControllerve isteyebilir, topViewControllerhatta kontrol edebilir UITabBarControllerve isteyebilirsiniz selectedViewController. Bu size şu anda kullanıcı tarafından görülebilen görünüm denetleyicisini verecektir.
Tricertops

33
Bu, tamamlanmamış bir çözümdür, çünkü childViewControllers (UINavigationController, UITabBarController, vb. Tarafından kullanıldığı gibi) hiyerarşisini değil, yalnızca kalıcı olarak sunulan görünüm denetleyicilerinin hiyerarşisini geçer.
alg

3
Bu, geçerli uygulama durumuna devam eden bir modsal görünüm denetleyicisinin sunumunu özetlemenin harika bir yoludur, benim durumumda uygulama zaman aşımına uğradığında bir parola yeniden girme ekranı idi. Teşekkürler!
erversteeg

11
@algal: gerçekten: UITabBarController, UINavigationController vardır zaten hiyerarşisinde en üst görünüm denetleyicileri. "En üstteki denetleyici" ile ne yapmak istediğinize bağlı olarak, onları hiç geçmek istemeyebilir ve içerikleriyle uğraşmak istemeyebilirsiniz . Benim durumumda her şeyin üstünde kalıcı bir denetleyici sunmaktı ve bunun için içeriklerini değil UINaviationController veya UITabBarController'ı almam gerekiyor !
Rick77

1
@ Rick77, bu doğruysa, buraya gömülmüş küçük bir yorumunuz diğer cevaplarda tonlarca karmaşık değişiklik yapılmasını gereksiz kılar. Başka hiç kimse bundan bahsetmediği için, bunun doğru olduğunu onaylamanızı istemem gerektiğini hissediyorum. Ve eğer öyleyse, kendi başına bir cevap olmayı hak etmesi çok önemlidir. Çünkü diğer cevapların büyük çoğunluğu bu sorunu çözmeye çalışırken geri tepiyor. Hayat kurtarabilirdin!
Le Mot Juiced

150

JonasG'nin yanıtını (geçiş yaparken sekme çubuğu denetleyicilerini bırakan) tamamlamak için , şu anda görünür olan görünüm denetleyicisini döndürme sürümüm :

- (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
Güzel, evet TabBar kontrol cihazlarını unuttum: P
JonasG

9
Dahil değilchildViewControllers
Awesome-o

Benim bak cevap davalarına yukarıdaki cevabı artırır aşağıda böyle popovers olarak dışında kalan @kleo, geçme ise diğer bazı görünüm denetleyicileri için subviews olarak eklenen görünüm denetleyicileri
Rajesh

Return [self topViewControllerWithRootViewController: navigationController.visibleViewController]; kullanıyorsanız, bir UIAlertController olsa bile sunulan görünüm denetleyicisini (IF ANY) döndüren visibleViewController. Ui uyarı denetleyicisinden kaçınması gereken biri için visibleViewController yerine topViewController kullanın
Johnykutty

Sadece bu benim 50 cent eklemek için - Ben bir webView yükleyen benim viewcontroller bu çalışma almak için mücadele ediyordu .. Bu çalışma alamadım nedeni görünümü hala hazır değildi çünkü (yükleme bitmedi) ve bu yüzden görünür değildi. UINavigationController henüz görünür bir ViewController yokken görünür bir ViewController almaya çalıştığı için topViewContoller almanın başarısız olduğu bir duruma yol açtı. Bu nedenle, herhangi bir kişi bu sorunla karşılaşırsa, yukarıdaki topViewController yöntemini çağırmadan önce görünümünüzün yüklenmesini tamamladığından emin olun.
mbuster

52

Farklı senaryolarla ilgilenen, özyinelemeli olmayan bir sürüm:

  • Görünüm denetleyicisi başka bir görünüm sunuyor
  • Görünüm denetleyicisi bir UINavigationController
  • Görünüm denetleyicisi bir 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;
     }
 }

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

2
Ne visibleViewControlleryaptığını netleştirmek için adlandırdım .
Jonny

31

Uzantıları kullanarak Swift için en çok görünüm denetleyicisine sahip olma

Kod:

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

Kullanımı:

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

mükemmel - bu çözüm için çok teşekkür ederim. Subviews'trick gerekiyordu! Tekrar, çok teşekkürler, günümü kurtardın.
iKK

25

Eric'in yanıtını tamamlamak için (popovers, gezinme denetleyicileri, sekme denetleyicileri, diğer bazı görünüm denetleyicilerine alt görünüm olarak eklenen görünüm denetleyicilerini gezerken), şu anda görünür olan görünüm denetleyicisini döndürme sürümüm:

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

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

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

Ve şimdi en iyi görünüm denetleyicisini almak için yapmanız gereken tek şey yukarıdaki yöntemi aşağıdaki gibi çağırmaktır:

UIViewController *topMostViewControllerObj = [self topViewController];

SplitViewController da eksik mi?
apinho

21

Bu cevap childViewControllerstemiz ve okunabilir bir uygulamayı içerir ve sürdürür.

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

Bazı kod güncellendi, nedeniyle de minimize ederek ve geri yükleyerek hangi denetleyicinin olduğunu gösterir. nik-kov-ios-developer.blogspot.ru/2016/12/…
Nik Kov

Hey, hadi, "topVisibleViewController" nerede?
Cennet

12

Kısa süre önce bu durumu, ağ durumu değiştiğinde, denetleyicinin görüntülendiği ve türünün (UINavigationController, klasik denetleyici veya özel görünüm denetleyicisi) ne olduğu bir bildirim görünümü göstermesi gereken bir projemde aldım.

Bu yüzden kodumu serbest bıraktım, ki bu oldukça kolay ve aslında bir protokole dayanıyor, böylece her konteyner denetleyicisi ile esnek. Son cevaplarla ilgili gibi görünüyor, ama çok esnek bir şekilde.

Kodu buradan alabilirsiniz: PPTopMostController

Ve kullanarak en üst denetleyici var

UIViewController *c = [UIViewController topMostController];

10

Bu Eric'in cevabında bir gelişme:

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) yardımcı bir işlevdir.

Şimdi yapmanız gereken tek şey aramak topMostController()ve en iyi UIViewController iade edilmelidir!


7
1983'ten beri söyleyebilirim. Objective-C'nin C ... içerdiğini unutmayın C fonksiyonlarında ObjC kodunun kaydırılması yaygın bir uygulamadır, bu yüzden bu Objective-C kodudur.
JonasG

@JonasG Merhaba Jonas, Hangi koşullarda ObjC kodunu C'ye kaydırmayı tercih edersiniz? Çünkü bazen C fonksiyonlarını böyle görüyorum ve kullanımı ayırt edemiyorum. C kodunun girilmesi herhangi bir performans avantajı sağlıyor mu?
OzBoz

1
@OzBoz Hangi sınıfa selfait olması gerektiği hemen belli olmadığı durumlarda .
adib

8

İşte bunu benim almam. @Stakenborg sayesinde UIAlertView'ı en üst denetleyici olarak almayı atlamanın yolunu işaret ettiği için

-(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'deki gibi adlandırma yöntemlerinden kaçınmalısınız . Bunun özel bir anlamı vardır (daha fazlası: cocoadevcentral.com/articles/000082.php ) ve kodunuzda bu gereksinimleri karşılamıyorsunuz.
Vive

7
@implementation UIWindow (Uzantılar)

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

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

    dönüş topController;
}

@son

Orijinal gönderide belirtilen koşulu karşıladığınızı sanmıyorum.
Hot Licks

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

Bunu kullandım, ancak birden fazla sunulan görünüm denetleyicisi olduğunda bozulduğunu unutmayın
Chuck Boris

7

En son Swift Sürümü için:
Bir dosya oluşturun, adlandırın UIWindowExtension.swiftve aşağıdaki snippet'i yapıştırın:

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
}

Her yerde kullanın:

if let topVC = getTopViewController() {

}

Cevabınızı çok fazla değiştirmek istemiyorum ama birkaç şey önereceğim. 1. UISplitViewController için destek ekleyin. 2. switchif if yerine kullanın . 3. Statik bir fonksiyona da ihtiyacınız olduğundan emin değilim, bunu beyan ettiğiniz ilk aşama seviyesinde kolayca yapabileceğinizi düşünüyorum. 4. Muhtemelen çok fazla küresel işlev yaratmamak en iyisidir, ancak bu bir zevk meselesidir. Küresel işlevin etkisini elde etmek için bir kod satırı kullanabilirsiniz:UIApplication.sharedApplication().delegate?.window?.visibleViewController
Jordan Smith

7

UIApplicationSwift için basit uzantı :

NOT:

Bu önemsiyor moreNavigationControlleriçindeUITabBarController

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

Basit kullanım:

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

EVET EVET EVET - topMostViewController'ı bulmak için web'de birçok çözüm var, ancak uygulamanız Daha Fazla sekmeli sekme çubuğuna sahipse, biraz farklı işlem yapmalısınız.
Andy Obusek

7

Görünür akımı tutmak için aşağıdaki uzantıyı kullanın UIViewController. Swift 4.0 ve üstü için çalıştı

Swift 4.0 ve Sonrası:

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

Nasıl kullanılır?

let objViewcontroller = UIApplication.topViewController()

Bu test presentedViewControllerilk olarak, UINavigationControllerve UITabBarControllervakalardan önce yapılmamalı mı? Aksi takdirde, bir görünüm denetleyicisi bir UINavigationControllerveya öğesinden kalıcı olarak sunulursa, UITabBarControllergörünen görünüm denetleyicisi olsa da üst görünüm denetleyicisi olarak döndürülmez.
Drew

4

Yine başka bir Swift çözümü

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 Uzantısı


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

Gibi herhangi bir yerden kullanın,

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

ya da benzeri,

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

UINavigationController, UITabBarController gibi sınıflara sığdır

Zevk almak!


1
@BilalBakhrom "Upvoted. Bence cevabınız en iyisidir. Doğrudan topViewController () yöntemini çağıramazsınız. UIApplication sınıfı singleton," paylaşılan "adlı bir örneği kullanın. içinde bir düzenleme ı reddetmek için oy ettik. Bu gerçekten doğruysa, bu düzenlemeyi onaylayabileceğiniz bir menüye gitmek için
wizzwizz4

3

İşte benim için işe yarayan.

KeyWindow'un uyarı gibi bazı işletim sistemi şeyleri olduğu için bazen denetleyicinin anahtar penceresinde nil olduğunu buldum.

 + (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'in cevabını genişleterek, keyWindow'un aslında istediğiniz pencere olmasına dikkat etmelisiniz. Örneğin uyarı görünümünde bir şeye dokunduktan sonra bu yöntemi kullanmaya çalışıyorsanız, keyWindow aslında uyarı penceresi olacaktır ve bu şüphesiz sizin için sorunlara neden olacaktır. Bu bir uyarı yoluyla derin bağlantıları ele alırken vahşi doğada oldu ve NO STACK TRACE ile SIGABRT'lere neden oldu. Hata ayıklamak için toplam kaltak.

İşte şimdi kullanıyorum kodu:

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

Bunu, bu sorudaki diğer cevaplardan istediğiniz üstten görünüm denetleyicisini almanın herhangi bir lezzeti ile karıştırmaktan çekinmeyin.


Bunun tam bir çözüm olduğunu buldunuz mu? Diğer cevapların birçoğu son derece karmaşıktır ve birçok kenar durumu açıklamaya çalışır. Bunun doğru olmasını istiyorum , çok basit ve zarif.
Le Mot Juiced

Onunla hiç problem yaşamadım. Nav yığınınızla olağandışı bir şey yapmıyorsanız, bu işe yaramalıdır, aksi takdirde diğer çözümlerin bazıları daha karmaşık vakaları ele alır.
Stakenborg

3

Alternatif Swift çözümü:

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

    return topController!
}

3

Bu çözüm en eksiksiz çözümdür. Şunları dikkate alır: UINavigationController UIPageViewController UITabBarController Ve üst görünüm denetleyicisinden sunulan en üstteki görünüm denetleyicisi

Örnek Swift 3'te.

3 aşırı yük var

//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'deki kısa ve özlü bir çözüm, UINavigationControllers , UITabBarControllers , sunulan ve alt görünüm denetleyicilerini dikkate alır :

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

Kullanımı:

let viewController = UIApplication.shared.topmostViewController()

2

Swift'te harika bir çözüm, AppDelegate'te uygulayın

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

Yanıtların çoğunun tamamen göz ardı edildiğini düşünüyorum UINavigationViewController, bu yüzden bu uygulamayı aşağıdaki uygulama ile ele aldım.

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

Çok geç olduğunu biliyorum ve gereksiz olabilir. Ama benim için çalışan parçacığı şöyle:

    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

Bu , herhangi bir kök görünüm denetiminden üst görünümController 1'i bulmak için harika çalışır

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

Bu, en üstteki görünüm denetleyicisini bularak gerçekleştirmeye çalıştığınız şeye yardımcı olup olmayacağından emin değilim, ancak yeni bir görünüm denetleyicisi sunmaya çalışıyordum, ancak kök görünüm denetleyicimde zaten kalıcı bir iletişim kutusu varsa, engellenirdi, bu yüzden bu kodu kullanarak tüm modsal görünüm denetleyicilerinin en üstüne dönecektir:

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

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

0

kullanarak en iyi görünüm denetleyicisini bulabilirsiniz

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

Bunun dışında, soruyu gerçekten okursanız self, hiçbir navigationControllerözelliği yoktur .
Hot Licks

0

Başka bir çözüm, ilk yanıtlayıcının ne olduğuna bağlı olarak çalışabilecek veya çalışmayabilecek yanıtlayıcı zincirine dayanır:

  1. İlk yanıtlayıcıyı alın .
  2. İlk yanıtlayıcıyla ilişkili UIViewController'ı edinin .

Örnek sözde kod:

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

Swift:

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

Kullanımı:

 if let topController = window.visibleViewController() {
            println(topController)
        }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.