सबसे ऊपर UIViewController जाओ


191

मैं UIViewControllerएक के लिए उपयोग के बिना सबसे ऊपर पाने के लिए प्रतीत नहीं कर सकते UINavigationController। यह है, जो कि अभी तक मेरे पास है:

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

हालाँकि, यह कुछ भी करने के लिए प्रतीत नहीं होता है। keyWindowऔर rootViewControllerगैर शून्य मान भी होने लगते हैं, तो वैकल्पिक श्रृंखलन एक मुद्दा नहीं होना चाहिए।

नोट: ऐसा कुछ करना एक बुरा विचार है। यह MVC पैटर्न को तोड़ता है।


यहाँ एक वैकल्पिक समाधान उपलब्ध है stackoverflow.com/a/39994115/1872233
iDevAmit

जवाबों:


284

presentViewControllerएक दृश्य नियंत्रक दिखाता है। यह एक दृश्य नियंत्रक वापस नहीं करता है। यदि आप एक का उपयोग नहीं कर रहे हैं UINavigationController, तो आप शायद देख रहे हैं presentedViewControllerऔर आपको रूट पर शुरू करने और प्रस्तुत विचारों के माध्यम से पुनरावृति करने की आवश्यकता होगी।

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

    // topController should now be your topmost view controller
}

स्विफ्ट 3+ के लिए:

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

    // topController should now be your topmost view controller
}

IOS 13+ के लिए

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
क्या कोई लूप समझा सकता है? मेरे लिए, ऐसा लगता है कि वहाँ कुछ भी नहीं है पर लूप; मुझे भी यकीन नहीं है कि यह क्यों संकलित है।
प्रोफेसर टॉम

15
@ProfistrTom लूप तब तक जारी रहता है जब तक कि topController.presentedViewControllerकुछ वापस नहीं हो जाता (यानी, नियंत्रक के पास एक प्रस्तुत बाल नियंत्रक है)। यह while letइस तथ्य को लागू करना है कि topController.presentedViewControllerकुछ वापस करना चाहिए। यदि यह शून्य देता है (यानी, यह नियंत्रक कोई प्रस्तुत बच्चे नहीं है), तो यह लूप बंद कर देगा। लूप के शरीर में, यह वर्तमान के रूप में बच्चे को पुन: सौंपता है topController, और फिर से लूप करता है, दृश्य नियंत्रक पदानुक्रम नीचे जा रहा है। topControllerजैसा varकि बाहरी ifकथन में है, यह पुनर्मूल्यांकन कर सकता है ।
रिक्शा

1
धन्यवाद। मुझे ऑनलाइन कोई भी उदाहरण नहीं मिला है while let। बेशक, बहुत सारे if letउदाहरण पाए जाते हैं।
प्रोफेसर टॉम

1
let x = somethingThatCouldBeNilवाक्य रचना कहीं भी उपयोग करने के लिए एक सच्चाई मूल्य / हालत इस्तेमाल किया जा सकता एक सुपर आसान चाल है। यदि हमने इसका उपयोग यहां नहीं किया है, तो हमें स्पष्ट रूप से एक मूल्य प्रदान करना होगा, फिर यह देखने के लिए परीक्षण करें कि क्या यह वास्तव में है। मुझे लगता है कि यह वास्तव में रसीला और अभिव्यंजक है।
रिक्शे की जूल

1
मैं चाल से परिचित हूं, यह छोरों में कारण के लिए बस थोड़ा अधिक कठिन है - जिसके लिए मुझे उदाहरणों की एक कमी मिली है-विशेष रूप से यह एक।
प्रोफेसर टॉम

271

यह विस्तार है

स्विफ्ट 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
    }
}

स्विफ्ट 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
    }
}

आप इसे अपने नियंत्रक पर कहीं भी उपयोग कर सकते हैं

if let topController = UIApplication.topViewController() {

}

1
आपके टिप के लिए धन्यवाद :)
Thein

4
मैंने इस उत्तर को एक महत्वपूर्ण संपादन करने का प्रयास किया, लेकिन यह अस्वीकृत हो गया (मुझे पता नहीं क्यों और दिए गए टेम्पलेट कारणों से कोई मतलब नहीं है): यह जाँचना महत्वपूर्ण है कि क्या nav.vanishViewController पुनरावर्ती में उपयोग करने से पहले शून्य है या नहीं कॉल (जैसे कि tab.selectedViewController की जाँच कैसे की जाती है) क्योंकि अन्यथा, अगर यह शून्य था, तो आप एक पुनरावर्ती अनंत लूप में आ जाएंगे।
ईथन जी

@ ईथन जी मेरी समझ के अनुसार, यदि nav.v अदृश्य व्यूकंट्रोलर शून्य है, तो फ़ंक्शन शून्य पर वापस आ जाएगा (अंतिम तक ड्रॉप return)। यह अनंत लूप में कैसे पहुंच सकता है?
डेसमंड डीएआई

3
मुझे लगता है कि इसे UIViewController के स्थिर कार्य के रूप में बनाना अधिक तर्कसंगत होगा
लेसज़ेक ज़र्ना

1
यदि आप UITabBarControllers पर सामान्य रूप से प्रस्तुत दृश्य नियंत्रकों को पकड़ना चाहते हैं, तो 'प्रस्तुत
ViewController

65

स्विफ्ट 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
    }
}

कैसे इस्तेमाल करे

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

2
शानदार समाधान। धन्यवाद!
एंड्रे एम।

2
'कीविन्डो' को iOS 13.0 में चित्रित किया गया था।
rs7 12

2
'कीविन्डो' को iOS 13.0 में चित्रित किया गया था stackoverflow.com/a/57899013/4514671 पर
रीक्रिएट किया गया

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

उपयोग:

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

यह समाधान वास्तव में आशाजनक लग रहा था, हालांकि मैंने इसे देखने के नियंत्रक को चलाने के लिए इसे चलाने की कोशिश की जब मैं एक धक्का अधिसूचना प्राप्त करता हूं और इस पर एक शून्य त्रुटि return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)
माइक

@ आपको केवल प्रस्तुत दृश्य नियंत्रक का उपयोग करने की आवश्यकता है, प्रस्तुत दृश्य नियंत्रक नहीं। प्रस्तुत
दृश्यकंट्रोलर

@ क्लेयर यदि आपने मोडल व्यू कंट्रोलर के शीर्ष पर एक मोडल व्यू कंट्रोलर प्रस्तुत किया है तो आपको आवश्यकता है .presentedViewController.presentedViewController, या नहीं?
बारन

6

डायनज़ उत्तर, ऑब्जेक्टिव-सी संस्करण पर आधारित है

- (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 में UINavigationController के लिए काम नहीं करेंगे। यह UINavigationController लौटेगा, नेविगेशन में अटके टॉपकंट्रोलर को वापस करना चाहिए।
माइक।

Tnx Tnx Tnx Bro
reza_khalafi

6

मुझे @ dianz का उत्तर बहुत पसंद आया , और इसलिए यहां स्विफ्ट 3 का संस्करण है। यह मूल रूप से एक ही बात है, लेकिन उसका कर्ली ब्रेस गायब था और कुछ वाक्य रचना / चर / विधि के नाम बदल गए हैं। तो यहाँ यह है!

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

उपयोग हालांकि अभी भी वैसा ही है:

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

6

https://gist.github.com/db0company/369bfa43cb84b145dfd8 मैंने इस साइट पर उत्तर और टिप्पणियों पर कुछ परीक्षण किए। मेरे लिए, निम्नलिखित कार्य करता है

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

फिर, शीर्ष दृश्य प्राप्त करें कंट्रोलर द्वारा:

UIApplication.shared.topMostViewController()

5

शीर्ष UIViewController खोजने के लिए इस कोड का उपयोग करें

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

2
यह रिक्शे के जवाब से कैसे अलग है?
इलेक्ट्रोबुधा

5

एक फ़ंक्शन के बजाय एक कम्प्यूटेड चर का उपयोग करके @AlberZou पर थोड़ा भिन्नता

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

तो कहो

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

4

ऊपर बॉब -c पर आधारित:

स्विफ्ट 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;
            }
        }
    }
}

3

बहुत सारे स्वाद लेकिन कोई भी पुनरावृत्त एक विस्तृत नहीं है। पिछले वाले से संयुक्त:

     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 में एक UIViewController चर को परिभाषित कर सकते हैं, और प्रत्येक दृश्य मेंWillAppear स्वयं के लिए चर सेट (हालांकि dianz जवाब सबसे अच्छा जवाब है।)

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

1
बहुत बहुत धन्यवाद यह मेरे लिए दूसरे समाधान के रूप में ठीक काम करता है जब यह नेविगेशन को पाने की कोशिश करता है तो इसे वापस लौटाएं क्योंकि मैं किसी भी नए वीसी को धक्का देने में सक्षम नहीं था
अमृत ​​गुस्से में

सुनिश्चित करें कि वर्तमान LV कमजोर संदर्भ के रूप में परिभाषित किया गया है, या आपके पास मेमोरी रिसाव होगा।
बुबसु

2

स्विफ्ट 3 में दृश्यमान दृश्य नियंत्रक को खोजने के लिए

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

     let prefs = UserDefaults.standard

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

     }
}

यह कोड अंतिम जोड़ा गया या अंतिम सक्रिय नियंत्रक दिखता है।

यह मैंने AppDelegate में सक्रिय दृश्य नियंत्रक खोजने के लिए उपयोग किया है


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

'दृश्यदर्शीकंट्रोलर' का अस्पष्ट उपयोग
उमर एन शामली

1

आपने कोड कहां रखा है?

मैं अपने कोड को अपने डेमो में आज़माता हूं, मुझे पता चला है, अगर आप कोड को अंदर डालते हैं

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

विफल हो जाएगा, क्योंकि कुंजी विंडो अभी तक सेटिंग कर रही है।

लेकिन मैंने आपका कोड कुछ व्यू कंट्रोलर में डाल दिया

override func viewDidLoad() {

यह सिर्फ काम करता है।


यह अंदर नहीं है didFinishLaunchingWithOptions। मुझे सिर्फ विभिन्न डिबग उद्देश्यों के लिए इसकी आवश्यकता है।
ज़ॉयट

1

एक बहुत ही दुर्लभ मामले में, कस्टम सेग के साथ, शीर्ष सबसे अधिक दृश्य नियंत्रक एक नेविगेशन स्टैक या टैब बार नियंत्रक में या प्रस्तुत नहीं किया गया है, लेकिन इसका दृश्य मुख्य विंडाउन के साक्षात्कारों के शीर्ष पर डाला गया है।

ऐसी स्थिति में, यह जांचना आवश्यक UIApplication.shared.keyWindow.subviews.last == self.viewहै कि क्या यह निर्धारित करने के लिए कि वर्तमान दृश्य नियंत्रक सबसे ऊपर है।


1

किसी को भी तेज 5 / iOS 13+ समाधान की तलाश है ( keywindowiOS 13 के बाद से पदावनत)

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

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

0

मेरे लिए सबसे अच्छा समाधान एक फ़ंक्शन के साथ एक विस्तार है। इस एक्सटेंशन के साथ एक स्विफ्ट फ़ाइल बनाएं

पहले UIWindow एक्सटेंशन है :

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
}

और यदि आप इसका उपयोग करना चाहते हैं, तो आप इसे कहीं भी कॉल कर सकते हैं। उदाहरण :

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

फ़ाइल कोड इस प्रकार है :

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
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.