En iyi UIViewController'ı alın


192

Bir UIViewControllererişim olmadan en üst almak gibi görünmüyor UINavigationController. Şimdiye kadar sahip olduğum şey:

UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(vc, animated: true, completion: nil)

Ancak, hiçbir şey yapmıyor gibi görünüyor. keyWindowVe rootViewControlleristeğe bağlı zincirleme bir sorun olmamalı bu yüzden, çok olmayan nil değerler olduğu görülüyor.

NOT: Böyle bir şey yapmak kötü bir fikirdir. MVC modelini kırar.


İşte alternatif bir çözüm mevcut stackoverflow.com/a/39994115/1872233
iDevAmit

Yanıtlar:


287

presentViewControllerbir görünüm denetleyicisini gösterir. Bir görünüm denetleyicisi döndürmez. A kullanmıyorsanız UINavigationController, büyük olasılıkla arıyoruz presentedViewControllerve kökten başlamanız ve sunulan görünümler boyunca tekrarlamanız gerekir.

if var topController = UIApplication.sharedApplication().keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}

Swift 3+ için:

if var topController = UIApplication.shared.keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

    // topController should now be your topmost view controller
}

İOS 13+ için

let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

if var topController = keyWindow?.rootViewController {
    while let presentedViewController = topController.presentedViewController {
        topController = presentedViewController
    }

// topController should now be your topmost view controller
}

1
Birisi while döngüsünü açıklayabilir mi? Bana göre, üzerinde dönecek bir şey yok gibi görünüyor; Bunun neden derlendiğinden bile emin değilim.
Profesör Tom

15
@ProfessorTom Döngü bir topController.presentedViewControllerşey döndürdüğü sürece devam eder (yani, denetleyicide sunulan bir çocuk denetleyicisi vardır). Bir şeylerin geri dönmesi gerektiği while letgerçeğini uygulamaktır topController.presentedViewController. Nil döndürürse (yani, bu denetleyicinin sunulan alt öğesi yoksa), döngüyü durdurur. Döngünün gövdesinde, alt öğeyi geçerli olarak yeniden atar topControllerve görünüm denetleyicisi hiyerarşisine inerek tekrar döngüler. Dış ifadede topControllerolduğu gibi yeniden atayabilir . varif
rickerbh

1
teşekkür ederim. Çevrimiçi olarak herhangi bir örnek bulamadım while let. Elbette if letbulunacak çok sayıda örnek var.
Profesör Tom

1
let x = somethingThatCouldBeNilSözdizimi bir doğruluk değeri / durum kullanılabilecek her yerde kullanmak için süper kullanışlı bir hile. Burada kullanmasaydık, açıkça bir değer atamamız ve ardından gerçekten orada olup olmadığını test etmemiz gerekirdi. Bence gerçekten özlü ve etkileyici.
rickerbh

1
Hünerlere aşinayım, döngüler sırasında akıl yürütmek biraz daha zordur - bunun için bir örnek eksikliği buldum - özellikle bu.
Profesör Tom

272

bu uzantıya sahip ol

Swift 2. *

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

Hızlı 3

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

Bunu kumandanızda herhangi bir yerde kullanabilirsiniz

if let topController = UIApplication.topViewController() {

}

1
Uzatma
ipucunuz

4
Bu cevap için önemli bir düzenleme yapmaya çalıştım, ancak reddedildi (neden ve hiçbir fikrim yok ve nedenler mantıklı gelmedi): Nav.visibleViewController özyinelemede kullanmadan önce nil olup olmadığını kontrol etmek önemlidir call (tıpkı tab.selectedViewController nasıl kontrol edilir gibi) çünkü aksi takdirde nil olsaydı, özyinelemeli sonsuz döngüye girersiniz.
Ethan G

@EthanG Anladığım kadarıyla, nav.visibleViewController nil ise, işlev nil (sonuncuya bırak) döndürecektir return. Sonsuz bir döngüye nasıl girebilir?
Desmond DAI

3
Bunu UIViewController
Leszek Zarna'nın

1
UITabBarControllers'da
kalıcı olarak

65

En hızlı görüntüyü elde etmek için hızlı 4/5

// MARK: UIApplication extensions

extension UIApplication {

    class func getTopViewController(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return getTopViewController(base: nav.visibleViewController)

        } else if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
            return getTopViewController(base: selected)

        } else if let presented = base?.presentedViewController {
            return getTopViewController(base: presented)
        }
        return base
    }
}

Nasıl kullanılır

if let topVC = UIApplication.getTopViewController() {
   topVC.view.addSubview(forgotPwdView)
}

2
Mükemmel çözüm. Teşekkürler!
Andrey

2
'keyWindow' iOS 13.0'da kullanımdan kaldırıldı.
rs7

2
'keyWindow' iOS 13.0'da kullanımdan kaldırıldı stackoverflow.com/a/57899013/4514671
Şubat'ta

19
extension UIWindow {

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

    static func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {
        if let navigationController = vc as? UINavigationController,
            let visibleController = navigationController.visibleViewController  {
            return UIWindow.getVisibleViewControllerFrom( vc: visibleController )
        } else if let tabBarController = vc as? UITabBarController,
            let selectedTabController = tabBarController.selectedViewController {
            return UIWindow.getVisibleViewControllerFrom(vc: selectedTabController )
        } else {
            if let presentedViewController = vc.presentedViewController {
                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)
            } else {
                return vc
            }
        }
    }
}

Kullanımı:

if let topController = window.visibleViewController() {
    println(topController)
}

Bu çözüm gerçekten umut verici görünüyordu, ancak ben bir push bildirimi aldığımda üzerinde olduğum görünüm denetleyicisi almak için bu çalıştırmak için çalıştı ve bir nil hata attıreturn UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)
Mike

@Mike, sundu değil, yalnızca sunulanViewController kullanmanız gerekir. presentedViewController
Allaire

@allaire Kalıcı görünüm denetleyicisinin üzerinde bir kalıcı görünüm denetleyicisi gösterdiyseniz .presentedViewController.presentedViewController, ihtiyacınız var mı yoksa değil mi?
Baran Emre

6

Dianz cevabına dayanarak, Objective-C versiyonu

- (UIViewController *) topViewController {
   UIViewController *baseVC = UIApplication.sharedApplication.keyWindow.rootViewController;
   if ([baseVC isKindOfClass:[UINavigationController class]]) {
       return ((UINavigationController *)baseVC).visibleViewController;
   }

   if ([baseVC isKindOfClass:[UITabBarController class]]) {
       UIViewController *selectedTVC = ((UITabBarController*)baseVC).selectedViewController;
       if (selectedTVC) {
           return selectedTVC;
       }
   }

   if (baseVC.presentedViewController) {
       return baseVC.presentedViewController;
   }
   return baseVC;
}

UITabBarController'da UINavigationController için çalışmaz. UINavigationController döndürür, topController gezinti sıkışmış dönmelidir.
Mike.R

Tnx Tnx Tnx Bro
reza_khalafi

6

@ Dianz'ın cevabını çok sevdim ve işte hızlı 3 versiyonu. Temelde aynı şey ama onun kıvırcık bir ayracı eksikti ve bazı sözdizimi / değişken / yöntem adları değişti. İşte burada!

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

Yine de kullanım hala aynıdır:

if let topController = UIApplication.topViewController() {
    print("The view controller you're looking at is: \(topController)")
}

6

https://gist.github.com/db0company/369bfa43cb84b145dfd8 Bu sitedeki cevaplar ve yorumlar hakkında bazı testler yaptım. Benim için aşağıdaki işler

extension UIViewController {
    func topMostViewController() -> UIViewController {

        if let presented = self.presentedViewController {
            return presented.topMostViewController()
        }

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

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

        return self
    }
}

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

Ardından, üst görünüm denetleyicisini şu yolla alın:

UIApplication.shared.topMostViewController()

5

En üstteki UIViewController'ı bulmak için bu kodu kullanın

func getTopViewController() -> UIViewController? {
    var topController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController
    while topController?.presentedViewController != nil {
        topController = topController?.presentedViewController
    }
    return topController
}

2
Bu rickerbh'ın cevabından nasıl farklı?
ElectroBuddha

5

@AlberZou'da bir işlev yerine hesaplanmış bir değişken kullanarak Hafif Varyasyon

extension UIViewController {
  var topMostViewController : UIViewController {

    if let presented = self.presentedViewController {
      return presented.topMostViewController
    }

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

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

    return self
  }
}

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

Sonra söyle

if let topViewControler = UIApplication.shared.topMostViewController {
    ... do stuff
}

4

Yukarıdaki Bob-c'ye dayanarak:

Swift 3.0

extension UIWindow {


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

    class func getVisibleViewControllerFrom(vc:UIViewController) -> UIViewController {

        if vc.isKind(of: UINavigationController.self) {

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

        } else if vc.isKind(of: UITabBarController.self) {

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

        } else {

            if let presentedViewController = vc.presentedViewController {

                return UIWindow.getVisibleViewControllerFrom(vc: presentedViewController)

            } else {

                return vc;
            }
        }
    }
}

4

Çok fazla lezzet var ama hiçbiri yinelemeli bir ayrıntı değil. Öncekilerden birleştirilmiş:

     func topMostController() -> UIViewController? {
        var from = UIApplication.shared.keyWindow?.rootViewController
        while (from != nil) {
            if let to = (from as? UITabBarController)?.selectedViewController {
                from = to
            } else if let to = (from as? UINavigationController)?.visibleViewController {
                from = to
            } else if let to = from?.presentedViewController {
                from = to
            } else {
                break
            }
        }
        return from
    }

2

AppDelegate'te bir UIViewController değişkeni tanımlayabilirsiniz ve her viewWillAppear değişkeni kendi kendine ayarlar. (ancak dianz yanıtı en iyi yanıttır.)

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)
    let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
    appDel.currentVC = self
}

1
çok teşekkürler o navigasyon almak için çalışırken diğer çözüm olarak benim için iyi çalışır.Nil dönüş kontrol böylece herhangi bir yeni vc itmek mümkün değildi
Amr Angry

CurrentVC'nin zayıf referans olarak tanımlandığından emin olun yoksa bellek sızıntısı yaşarsınız.
bubuxu

2

Swift 3'te görünür görünümü denetleyici bulmak için

if let viewControllers = window?.rootViewController?.childViewControllers {

     let prefs = UserDefaults.standard

     if viewControllers[viewControllers.count - 1] is ABCController{
        print("[ABCController] is visible")

     }
}

Bu kod, son eklenen veya son etkin denetleyiciyi görünür olarak bulur.

Bu aktif görünüm denetleyicisi bulmak için AppDelegate kullandım


2
import UIKit

extension UIApplication {

    // MARK: Choose keyWindow as per your choice
    var currentWindow: UIWindow? {
        connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
    }

    // MARK: Choose keyWindow as per your choice
    var keyWindow: UIWindow? {
        UIApplication.shared.windows.first { $0.isKeyWindow }
    }

    class func topMostViewController(base: UIViewController? = UIApplication.shared.currentWindow?.rootViewController) -> UIViewController? {

        if let nav = base as? UINavigationController {
            return topMostViewController(base: nav.visibleViewController)
        }

        if let tab = base as? UITabBarController {
            let moreNavigationController = tab.moreNavigationController

            if let top = moreNavigationController.topViewController, top.view.window != nil {
                return topMostViewController(base: top)
            } else if let selected = tab.selectedViewController {
                return topMostViewController(base: selected)
            }
        }
        if let presented = base?.presentedViewController {
            return topMostViewController(base: presented)
        }
        return base
    }
}

'VisibleViewController'ın belirsiz kullanımı
Omar N Shamali

1

Kodu nereye koydunuz?

Kodumu demomda deniyorum, öğrendim, eğer kodu koyarsanız

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { 

Anahtar penceresi henüz ayarlanmadığı için başarısız olur.

Ancak kodunuzu bazı görünüm denetleyicilerine koydum

override func viewDidLoad() {

Sadece çalışıyor.


İçinde değil didFinishLaunchingWithOptions. Buna sadece çeşitli hata ayıklama amaçları için ihtiyacım var.
Zoyt

1

Çok nadir bir durumda, özel segue ile, en üstteki görünüm denetleyicisi bir gezinme yığını veya sekme çubuğu denetleyicisinde değildir veya sunulur, ancak görünümü anahtar windown alt görünümlerinin üstüne eklenir.

Bu durumda, UIApplication.shared.keyWindow.subviews.last == self.viewgeçerli görünüm denetleyicisinin en üstte olup olmadığını kontrol etmek gerekir .


1

Hızlı bir 5 / iOS 13+ çözümü arayan herkes için ( keywindowiOS 13'ten beri kullanımdan kaldırılmıştır)

extension UIApplication {

    class func getTopMostViewController() -> UIViewController? {
        let keyWindow = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
        if var topController = keyWindow?.rootViewController {
            while let presentedViewController = topController.presentedViewController {
                topController = presentedViewController
            }
            return topController
        } else {
            return nil
        }
    }
}

Nasıl Kullanırım?
Chris Comas

Sadece böyle söyle. UIApplication.getTopMostViewController()ViewController'ınızın içinde. @ChrisComas
Virendra

0
  var topViewController: UIViewController? {
        guard var topViewController = UIApplication.shared.keyWindow?.rootViewController else { return nil }
        while let presentedViewController = topViewController.presentedViewController {
            topViewController = presentedViewController
        }
        return topViewController
    }

0

Benim için en iyi çözüm, işlevli bir uzantıdır. Bu uzantıyla hızlı bir dosya oluşturun

Birincisi UIWindow uzantısıdır :

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

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

bu dosya ekleme işlevi

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

Ve kullanmak istiyorsanız, istediğiniz yere çağırabilirsiniz. Örnek :

  override func viewDidLoad() {
    super.viewDidLoad()
      if let topVC = visibleViewController() {
             //show some label or text field 
    }
}

Dosya kodu şöyledir :

import UIKit

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

    static func visibleVC(vc: UIViewController?) -> UIViewController? {
        if let navigationViewController = vc as? UINavigationController {
            return UIWindow.visibleVC(vc: navigationViewController.visibleViewController)
        } else if let tabBarVC = vc as? UITabBarController {
            return UIWindow.visibleVC(vc: tabBarVC.selectedViewController)
        } else {
            if let presentedVC = vc?.presentedViewController {
                return UIWindow.visibleVC(vc: presentedVC)
            } else {
                return vc
            }
        }
    }
}

func visibleViewController() -> UIViewController? {
    let appDelegate = UIApplication.shared.delegate
    if let window = appDelegate!.window {
        return window?.visibleViewController
    }
    return nil
}
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.