Nhận được nhiều UIViewControll hàng đầu


191

Tôi dường như không thể có được nhiều nhất UIViewControllermà không có quyền truy cập vào a UINavigationController. Đây là những gì tôi có cho đến nay:

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

Tuy nhiên, nó dường như không làm gì cả. Giá trị keyWindowrootViewControllerdường như cũng không phải là con số không, vì vậy, chuỗi không bắt buộc không phải là một vấn đề.

LƯU Ý: Đó là một ý tưởng tồi để làm một cái gì đó như thế này. Nó phá vỡ mô hình MVC.


Đây là một giải pháp thay thế có sẵn stackoverflow.com/a/39994115/1872233
iDevAmit

Câu trả lời:


283

presentViewControllerhiển thị một bộ điều khiển xem. Nó không trả về bộ điều khiển xem. Nếu bạn không sử dụng UINavigationController, có lẽ bạn đang tìm kiếm presentedViewControllervà bạn sẽ cần bắt đầu từ gốc và lặp lại qua các chế độ xem được trình bày.

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

    // topController should now be your topmost view controller
}

Dành cho Swift 3+:

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

    // topController should now be your topmost view controller
}

Dành cho 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
Ai đó có thể giải thích vòng lặp while? Đối với tôi, có vẻ như không có gì để lặp lại; Tôi thậm chí không chắc chắn tại sao điều này biên dịch.
Giáo sư Tom

15
@ProfigatorTom Vòng lặp tiếp tục miễn là topController.presentedViewControllertrả về một cái gì đó (nghĩa là bộ điều khiển có bộ điều khiển con được trình bày). Đó là while letđể thực thi thực tế là topController.presentedViewControllerphải trả lại một cái gì đó. Nếu nó trả về nil (nghĩa là bộ điều khiển này không có con nào được trình bày), thì nó sẽ dừng lặp. Trong phần thân của vòng lặp, nó gán lại cho đứa trẻ như hiện tại topControllervà lặp lại, đi xuống hệ thống phân cấp của bộ điều khiển xem. Nó có thể gán lại topControllernhư là một tuyên bố varbên ngoài if.
rickerbh

1
cảm ơn bạn. Tôi không thể tìm thấy bất kỳ ví dụ trực tuyến nào while let. Tất nhiên, có rất nhiều if letví dụ được tìm thấy.
Giáo sư Tom

1
Các let x = somethingThatCouldBeNilcú pháp là một thủ thuật siêu tiện dụng để sử dụng bất cứ nơi nào một giá trị chân lý / điều kiện có thể được sử dụng. Nếu chúng tôi không sử dụng nó ở đây, chúng tôi sẽ phải gán một giá trị rõ ràng, sau đó kiểm tra xem nó có thực sự ở đó không. Tôi nghĩ rằng nó thực sự cô đọng và biểu cảm.
rickerbh

1
Tôi quen với mánh khóe này, khó hiểu hơn một chút trong khi loops mà tôi đã tìm thấy rất nhiều ví dụ về trò chơi, đặc biệt là ví dụ này.
Giáo sư Tom

270

có phần mở rộng này

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

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

Bạn có thể sử dụng nó ở bất cứ đâu trên bộ điều khiển của bạn

if let topController = UIApplication.topViewController() {

}

1
Cảm ơn mẹo mở rộng của bạn :)
Thein

4
Tôi đã cố thực hiện một chỉnh sửa quan trọng cho câu trả lời này, nhưng nó đã bị từ chối (tôi không biết tại sao và lý do mẫu được đưa ra không có ý nghĩa gì): Điều quan trọng là phải kiểm tra xem nav.visibleViewContoder có trước không khi sử dụng nó trong đệ quy gọi (giống như cách tab.selectedViewControll được kiểm tra) bởi vì nếu không, nếu không, bạn sẽ vào một vòng lặp vô hạn đệ quy.
Ethan G

@EthanG Theo sự hiểu biết của tôi, nếu nav.visibleViewControll là nil, hàm sẽ trả về nil (thả đến cuối cùng return). Làm thế nào nó có thể vào một vòng lặp vô hạn?
Desmond DAI

3
Tôi nghĩ sẽ hợp lý hơn khi biến nó thành chức năng tĩnh của UIViewControll
Leszek Zarna 17/2/2017

1
Kiểm tra 'PresentViewContoder' có thể xuất hiện trước nếu bạn muốn bắt các bộ điều khiển xem được trình bày một cách vừa phải trên UITabBarControllers ..
Tokuriku

65

Đối với swift 4/5 + để có được viewContoder trên cùng

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

Cách sử dụng

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

2
Giải pháp rực rỡ. Cảm ơn!
Andrey M.

2
'keyWindow' không được dùng nữa trong iOS 13.0.
rs7

2
'keyWindow' không được dùng nữa trong stackoverflow
13.0.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
            }
        }
    }
}

Sử dụng:

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

giải pháp này có vẻ rất hứa hẹn, tuy nhiên tôi đã cố chạy nó để có được bộ điều khiển xem khi tôi nhận được thông báo đẩy và nó đã gây ra lỗi khôngreturn UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)
Mike

@Mike bạn chỉ cần sử dụng PresentViewControll, không được trình bàyViewControll. đã trình
bàyViewControll

@allaire Nếu bạn đã trình bày một bộ điều khiển chế độ xem trên đầu của một bộ điều khiển xem chế độ thì bạn cần .presentedViewController.presentedViewControllerhay không?
Baran Emre

6

Dựa trên câu trả lời của Dianz, phiên bản Objective-C

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

Sẽ không hoạt động cho UINavestionControll trong UITabBarControll. nó sẽ trả về UINavestionContaptor, nên trả về topContoder trong điều hướng bị kẹt.
Mike.R

Tnx Tnx Tnx Bro
reza_khalafi

6

tôi đã yêu câu trả lời của @ dianz , và đây là phiên bản 3 của swift. Về cơ bản, đó là điều tương tự nhưng anh ta đã thiếu một dấu ngoặc nhọn và một số tên cú pháp / biến / phương thức đã thay đổi. Vì vậy, đây là!

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

Cách sử dụng vẫn giống hệt nhau mặc dù:

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

6

https://gist.github.com/db0company/369bfa43cb84b145dfd8 Tôi đã thực hiện một số thử nghiệm về câu trả lời và nhận xét trên trang web này. Đối với tôi, các công việc sau đây

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

Sau đó, có được viewControll hàng đầu bằng cách:

UIApplication.shared.topMostViewController()

5

Sử dụng mã này để tìm UIViewControll hàng đầu

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

2
Điều này khác với câu trả lời của rickerbh như thế nào?
ElectroBuddha

5

Biến đổi nhẹ trên @AlberZou bằng cách sử dụng biến được tính toán thay vì hàm

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

Vậy thì nói đi

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

4

Dựa trên Bob -c ở trên:

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

3

Quá nhiều hương vị nhưng không một hương vị lặp đi lặp lại. Kết hợp từ những cái trước:

     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

bạn có thể xác định biến UIViewControll trong AppDelegate và trong mỗi viewWillAppear đặt biến thành tự. (tuy nhiên câu trả lời dianz là câu trả lời tốt nhất.)

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

1
cảm ơn rất nhiều, nó hoạt động tốt với tôi như một giải pháp khác khi nó cố gắng điều hướng. Kiểm soát nó trở lại cho đến khi tôi không thể đẩy bất kỳ vc mới nào
Amr Angry

Đảm bảo hiện tại được định nghĩa là tham chiếu yếu, nếu không bạn sẽ bị rò rỉ bộ nhớ.
bubuxu

2

Để tìm viewContaptor hiển thị trong Swift 3

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

     let prefs = UserDefaults.standard

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

     }
}

Mã này tìm thấy bộ điều khiển hoạt động được thêm vào cuối cùng hoặc cuối cùng có thể nhìn thấy.

Cái này tôi đã sử dụng trong AppDelegate để tìm Trình điều khiển xem hoạt động


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

Việc sử dụng không rõ ràng ''ViewViewContaptor'
Omar N Shamali

1

Bạn đã đặt mã ở đâu?

Tôi thử mã của bạn trong bản demo của tôi, tôi phát hiện ra, nếu bạn đặt mã vào

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

sẽ thất bại, vì cửa sổ chính đã được thiết lập.

Nhưng tôi đặt mã của bạn trong một số trình điều khiển xem

override func viewDidLoad() {

Nó chỉ hoạt động.


Nó không ở trong didFinishLaunchingWithOptions. Tôi chỉ cần điều này cho các mục đích gỡ lỗi khác nhau.
Zoyt

1

Trong một trường hợp rất hiếm, với segue tùy chỉnh, bộ điều khiển chế độ xem nhiều nhất không nằm trong ngăn xếp điều hướng hoặc bộ điều khiển thanh tab hoặc được trình bày, nhưng chế độ xem của nó được chèn vào đầu các bản xem trước của Windown.

Trong tình huống như vậy, cần kiểm tra xem có UIApplication.shared.keyWindow.subviews.last == self.viewxác định xem bộ điều khiển chế độ xem hiện tại có cao nhất không.


1

Đối với bất cứ ai đang tìm kiếm một giải pháp 5 / iOS 13+ nhanh chóng ( keywindowkhông dùng nữa kể từ iOS 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

Giải pháp tốt nhất cho tôi là một phần mở rộng với chức năng. Tạo một tập tin nhanh chóng với phần mở rộng này

Đầu tiên là phần mở rộng 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
            }
        }
    }
}

bên trong tập tin đó thêm chức năng

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

Và nếu bạn muốn sử dụng nó, bạn có thể gọi nó ở bất cứ đâu. Ví dụ :

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

Mã tập tin là như thế này :

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
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.