Cách tìm bộ điều khiển xem trên cùng trên iOS


253

Bây giờ tôi đã gặp một vài trường hợp, nơi có thể thuận tiện để có thể tìm thấy bộ điều khiển chế độ xem "trên cùng" (bộ chịu trách nhiệm cho chế độ xem hiện tại), nhưng chưa tìm được cách nào để làm điều đó.

Về cơ bản thách thức là thế này: Cho rằng một người đang thực thi trong một lớp không phải là trình điều khiển khung nhìn (hoặc khung nhìn) [và không có địa chỉ của chế độ xem đang hoạt động] và chưa được chuyển qua địa chỉ của trình điều khiển chế độ xem trên cùng ( hoặc, giả sử, địa chỉ của bộ điều khiển điều hướng), có thể tìm thấy bộ điều khiển xem đó không? (Và, nếu vậy, làm thế nào?)

Hoặc, thất bại trong đó, có thể tìm thấy chế độ xem trên cùng?


Vì vậy, bạn đang nói rằng nó không thể.
Licks nóng

@Daniel không, tôi đang nói rằng có vẻ như mã của bạn có thể sử dụng một số thiết kế lại, bởi vì bạn hiếm khi cần phải biết điều này. Ngoài ra, ý tưởng "trên cùng" chỉ có giá trị trong một số bối cảnh nhất định, và thậm chí sau đó không phải lúc nào cũng vậy.
Dave DeLong

@Daniel Tôi đã đọc sai câu hỏi của bạn. Có rất nhiều if và buts đang cố gắng trả lời cái này. Nó phụ thuộc vào lưu lượng điều khiển xem của bạn. Câu trả lời của @ Wilbur nên là điểm khởi đầu tốt để truy tìm nó.
Deepak Danduprolu

Vâng, hãy đơn giản hóa nó cho một trường hợp cụ thể. Nếu tôi muốn viết một bản sao của UIAlertView, tôi sẽ làm thế nào? Lưu ý rằng nó có thể hoạt động tốt mà không được chuyển bất kỳ địa chỉ nào cho các bộ điều khiển hoặc khung nhìn khác.
Licks nóng

4
@Daniel: Thêm UIWindow thứ hai hoạt động tốt cho các lớp phủ giống như cảnh báo.
Wilbur Vandrsmith

Câu trả lời:


75

iOS 4 đã giới thiệu thuộc tính rootViewControll trên UIWindow:

[UIApplication sharedApplication].keyWindow.rootViewController;

Bạn sẽ cần phải tự thiết lập nó sau khi bạn tạo bộ điều khiển xem.


155
Wilbur, điều này sẽ cung cấp cho bạn ngược lại với những gì op yêu cầu. rootViewControll là trình điều khiển khung nhìn cơ sở chứ không phải là đỉnh nhất.
m4rkk

3
m4rkk: "Top-most" phụ thuộc vào hướng bạn nhìn từ đâu. Các bộ điều khiển mới có được thêm vào đầu (giống như ngăn xếp) hoặc dưới cùng (giống như cây) không? Trong mọi trường hợp, OP đã đề cập đến bộ điều khiển điều hướng ở trên đỉnh, ngụ ý chế độ xem hướng xuống.
Wilbur Vandrsmith

50
Word, top đầu được sử dụng cho trình điều khiển xem, đó là trực quan trên đầu (như -[UINavigationController topViewController]). Sau đó, có từ „root, đó là gốc của cây (như -[UIWindow rootViewController].
Tricertops

13
@ImpurestClub Tôi không thể tìm thấy trong tài liệu này , Xcode dường như không tìm thấy nó.
Drux

4
@adib không, nó thuộc về UINavestionControll
David H

428

Tôi nghĩ bạn cần kết hợp câu trả lời được chấp nhận và @ fishstix's

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

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

    return topController;
}

Swift 3.0+

func topMostController() -> UIViewController? {
    guard let window = UIApplication.shared.keyWindow, let rootViewController = window.rootViewController else {
        return nil
    }

    var topController = rootViewController

    while let newTopController = topController.presentedViewController {
        topController = newTopController
    }

    return topController
}

4
Ngoài ra, bạn có thể kiểm tra UINavigationControllervà yêu cầu topViewControllerhoặc thậm chí kiểm tra UITabBarControllervà yêu cầu selectedViewController. Điều này sẽ giúp bạn có được bộ điều khiển xem hiện đang hiển thị cho người dùng.
Tricertops

33
Đây là một giải pháp chưa hoàn chỉnh, vì nó chỉ đi qua hệ thống phân cấp của các bộ điều khiển xem được trình bày theo phương thức, chứ không phải là hệ thống phân cấp của childViewControllers (như được sử dụng bởi UINavestionControll, UITabBarContoder, v.v.).
tảo

3
Đây là một cách tuyệt vời để trừu tượng hóa việc trình bày bộ điều khiển chế độ xem mô đun tiếp tục trạng thái ứng dụng hiện tại, trong trường hợp của tôi, đó là màn hình thử lại mật khẩu sau khi ứng dụng hết thời gian. Cảm ơn!
erversteeg

11
@algal: not really: UITabBarController, UINavigationController đã điều khiển xem trên cùng trong hệ thống phân cấp. Tùy thuộc vào những gì bạn muốn làm với "bộ điều khiển trên cùng", bạn có thể không muốn đi qua chúng và mân mê nội dung của chúng. Trong trường hợp của tôi, nó là để trình bày một bộ điều khiển phương thức trên tất cả mọi thứ, và vì thế tôi cần phải có UINaviationContoder hoặc UITabBarContoder chứ không phải nội dung của chúng !!
Rick77

1
@ Rick77, nếu điều này là đúng, một bình luận nhỏ của bạn bị chôn vùi ở đây cho thấy không cần thiết hàng tấn sửa đổi phức tạp trong các câu trả lời khác. Vì không ai khác đề cập đến điều này cả, tôi cảm thấy tôi phải yêu cầu bạn xác nhận rằng đó là sự thật. Và nếu có, nó quan trọng đến mức nó xứng đáng là một câu trả lời của riêng nó. Bởi vì phần lớn các câu trả lời khác làm backflips cố gắng giải quyết vấn đề này. Bạn sẽ được cứu sống!
Le Mot Juated

150

Để hoàn thành câu trả lời của JonasG (người đã bỏ qua bộ điều khiển thanh tab trong khi di chuyển ngang), đây là phiên bản trả về bộ điều khiển chế độ xem hiện tại của tôi:

- (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
Thật tuyệt, vâng tôi quên mất bộ điều khiển TabBar: P
JonasG

9
Không bao gồmchildViewControllers
Awesome-o

Hãy xem câu trả lời của tôi dưới đây để cải thiện câu trả lời ở trên bằng cách xử lý các trường hợp @kleo bỏ qua như popovers, xem bộ điều khiển được thêm dưới dạng xem xét cho một số bộ điều khiển xem khác trong khi di chuyển ngang
Rajesh

Nếu bạn đang sử dụng return [self topViewControllWithRootViewControll: navigationContoder.visibleViewCont điều khiển];, Đối với ai đó cần tránh bộ điều khiển cảnh báo ui, hãy sử dụng topViewContoder thay vì thấyViewContaptor
Johnykutty

Chỉ để thêm 50 xu của tôi vào đây - Tôi đã vật lộn để làm việc này trong trình điều khiển chế độ xem của tôi tải webView .. lý do tại sao tôi không thể làm việc này là vì chế độ xem vẫn chưa sẵn sàng (không tải xong) và do đó nó không thể nhìn thấy. Điều đó dẫn đến một tình huống khiến việc sử dụng topViewContoller không thành công, bởi vì UINavestionControll đang cố gắng để có được một ViewContoder hiển thị trong khi chưa có ViewContoder hiển thị. Vì vậy, nếu bất cứ ai phải đối mặt với vấn đề này, hãy đảm bảo rằng chế độ xem của bạn kết thúc tải trước khi bạn thực hiện cuộc gọi đến phương thức topViewControll ở trên.
mbuster

52

Một phiên bản hoàn chỉnh không đệ quy, xử lý các tình huống khác nhau:

  • Trình điều khiển khung nhìn đang hiển thị một khung nhìn khác
  • Bộ điều khiển xem là một UINavigationController
  • Bộ điều khiển xem là một UITabBarController

Mục tiêu-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
Tôi đặt tên cho nó visibleViewControllerđể làm cho nó rõ ràng những gì nó làm.
Jonny

31

Bắt đầu bộ điều khiển xem nhiều nhất cho Swift bằng tiện ích mở rộng

Mã số:

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

Sử dụng:

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

tuyệt vời - cảm ơn bạn rất nhiều cho giải pháp này. Các cuộc phỏng vấn là cần thiết! Một lần nữa, cảm ơn rất nhiều, bạn đã cứu ngày của tôi.
iKK

25

Để hoàn thành của Eric câu trả lời (người đã rời khỏi popovers, điều khiển chuyển hướng, tabbarcontrollers, xem các bộ điều khiển thêm vào như subviews để một số bộ điều khiển xem khác trong khi vượt qua), đây là phiên bản của tôi trở về bộ điều khiển xem hiện thời hiển thị:

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

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

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

Và bây giờ, tất cả những gì bạn cần làm để có được bộ điều khiển xem hàng đầu nhất là gọi phương thức trên như sau:

UIViewController *topMostViewControllerObj = [self topViewController];

Thiếu SplitViewControll là tốt?
apinho

21

Câu trả lời này bao gồm childViewControllersvà duy trì việc thực hiện sạch sẽ và dễ đọc.

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

Đã cập nhật một số mã, do quá hiển thị bộ điều khiển đó là gì bằng cách giảm thiểu và khôi phục lại nó. nik-kov-ios-developer.blogspot.ru/2016/12/ trên
Nik Kov

Này, thôi nào, "topVisibleViewControll" của bạn ở đâu?
Thiên đường

12

Gần đây tôi đã gặp tình huống này trong một dự án của mình, yêu cầu hiển thị chế độ xem thông báo cho dù bộ điều khiển hiển thị là gì và loại nào (UINavestionContaptor, bộ điều khiển cổ điển hoặc bộ điều khiển xem tùy chỉnh), khi trạng thái mạng thay đổi.

Vì vậy, tôi đã phát hành mã của mình, điều này khá dễ dàng và thực sự dựa trên một giao thức để nó linh hoạt với mọi loại bộ điều khiển container. Nó dường như có liên quan với các câu trả lời cuối cùng, nhưng theo một cách linh hoạt hơn nhiều.

Bạn có thể lấy mã ở đây: PPTopMostControll

Và có bộ điều khiển hàng đầu sử dụng

UIViewController *c = [UIViewController topMostController];

10

Đây là một cải tiến cho câu trả lời của Eric:

UIViewController *_topMostController(UIViewController *cont) {
    UIViewController *topController = cont;

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

    if ([topController isKindOfClass:[UINavigationController class]]) {
        UIViewController *visible = ((UINavigationController *)topController).visibleViewController;
        if (visible) {
            topController = visible;
        }
    }

    return (topController != cont ? topController : nil);
}

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

    UIViewController *next = nil;

    while ((next = _topMostController(topController)) != nil) {
        topController = next;
    }

    return topController;
}

_topMostController(UIViewController *cont) là một chức năng của người trợ giúp.

Bây giờ, tất cả những gì bạn cần làm là gọi topMostController()và trả về UIViewControll hàng đầu!


7
Từ năm 1983 tôi sẽ nói. Hãy nhớ rằng Objective-C chứa C ... Gói mã ObjC trong các hàm C là một cách làm phổ biến, vì vậy, đây là mã Objective-C.
JonasG

@JonasG Xin chào Jonas, Trong trường hợp nào bạn thích gói mã ObjC trong C? Bởi vì, đôi khi tôi thấy các hàm C như thế này và không thể phân biệt việc sử dụng. Mã gói trong C có cung cấp bất kỳ lợi ích hiệu suất nào không?
OzBoz

1
@OzBoz Trong những tình huống không rõ ràng selfnên thuộc về lớp nào .
adib

8

Đây là của tôi về điều này. Cảm ơn @Stakenborg đã chỉ ra cách bỏ qua việc lấy UIAlertView làm bộ điều khiển hàng đầu nhất

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

Bạn nên tránh các phương thức đặt tên như getSomething:trong Objective-C. Điều này có một ý nghĩa đặc biệt (thêm: cocoadevcentral.com/articles/000082.php ) và bạn không đáp ứng các yêu cầu này trong mã của mình.
Vive

7
@imâyation UIWindow (Tiện ích mở rộng)

- (UIViewContoder *) topMostControll
{
    UIViewControll * topContoder = [self rootViewControll];

    while (topControll.presentsViewControll) {
        topControll = topControll.presentsViewCont điều khiển;
    }

    trả về topContoder;
}

@kết thúc

Tôi không nghĩ rằng bạn hài lòng với điều kiện được nêu trong bài viết gốc.
Licks nóng

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

Tôi đã sử dụng cái này, nhưng lưu ý rằng nó bị hỏng khi có nhiều hơn một bộ điều khiển xem được trình bày
Chuck Boris

7

Đối với Phiên bản Swift mới nhất:
Tạo một tệp, đặt tên cho nó UIWindowExtension.swiftvà dán đoạn mã sau:

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
}

Sử dụng nó ở bất cứ đâu như:

if let topVC = getTopViewController() {

}

Tôi không muốn thay đổi câu trả lời của bạn quá nhiều nhưng sẽ gợi ý một vài điều. 1. Thêm hỗ trợ cho UISplitViewControll. 2. sử dụng switchthay vì nếu khác. 3. không chắc chắn bạn cũng cần một hàm tĩnh, tôi nghĩ bạn có thể làm điều này một cách dễ dàng trong trường hợp cấp độ var đầu tiên mà bạn đã khai báo. 4. Có lẽ tốt nhất không nên tạo quá nhiều chức năng toàn cầu nhưng đó là vấn đề của hương vị. Bạn có thể sử dụng một dòng mã để đạt được hiệu ứng của hàm toàn cầu:UIApplication.sharedApplication().delegate?.window?.visibleViewController
Jordan Smith

7

Tiện ích mở rộng đơn giản cho UIApplicationtrong Swift:

GHI CHÚ:

Nó quan tâm đến moreNavigationControllerbên trongUITabBarController

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

Cách sử dụng đơn giản:

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

CÓ CÓ CÓ - có nhiều giải pháp trên web để tìm topMostViewControll nhưng nếu ứng dụng của bạn có thanh tab với tab Khác, BẠN PHẢI xử lý nó một chút khác nhau.
Andy Obusek

7

Sử dụng phần mở rộng dưới đây để lấy hiện tại có thể nhìn thấy UIViewController. Làm việc cho Swift 4.0 trở lên

Swift 4.0 trở lên:

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

Sử dụng như thế nào?

let objViewcontroller = UIApplication.topViewController()

Không nên thử nghiệm này cho presentedViewControllerlần đầu tiên, trước UINavigationControllerUITabBarControllercác trường hợp? Mặt khác, nếu bộ điều khiển chế độ xem được trình bày một cách vừa phải từ một UINavigationControllerhoặc UITabBarController, nó sẽ không được trả về làm bộ điều khiển chế độ xem hàng đầu, mặc dù đó là bộ điều khiển chế độ xem có thể nhìn thấy.
vẽ

4

Một giải pháp Swift khác

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

Tiện ích mở rộng Swift 4.2


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

Sử dụng nó từ bất cứ nơi nào như,

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

hoặc thích,

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

Phù hợp với bất kỳ lớp nào như UINavestionControll, UITabBarControll

Thưởng thức!


1
@BilalBakhrom nói "Upvote. Tôi nghĩ câu trả lời của bạn là tốt nhất. Bạn không thể gọi trực tiếp phương thức topViewContoder (). Lớp UIApplication là singleton, sử dụng một thể hiện gọi là" shared "." trong một chỉnh sửa mà tôi đã bỏ phiếu để từ chối. Nếu điều này thực sự chính xác, xin vui lòng bấm vào đây để được đưa đến một menu nơi bạn có thể phê duyệt chỉnh sửa đó.
wizzwizz4

3

Đây là những gì làm việc cho tôi.

Tôi thấy rằng đôi khi bộ điều khiển không ở trên cửa sổ khóa, vì keyWindow là một số thứ giống như hệ điều hành, v.v.

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

Mở rộng câu trả lời của @ Eric, bạn cần cẩn thận rằng keyWindow thực sự là cửa sổ bạn muốn. Ví dụ, nếu bạn đang cố gắng sử dụng phương pháp này sau khi chạm vào một cái gì đó trong chế độ xem cảnh báo, keyWindow sẽ thực sự là cửa sổ cảnh báo và điều đó sẽ gây ra sự cố cho bạn. Điều này đã xảy ra với tôi trong tự nhiên khi xử lý các liên kết sâu thông qua một cảnh báo và gây ra SIGABRT với NO STACK TRACE. Tổng số chó cái để gỡ lỗi.

Đây là mã tôi đang sử dụng:

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

Hãy thoải mái kết hợp điều này với bất kỳ hương vị nào của việc truy xuất bộ điều khiển chế độ xem hàng đầu mà bạn thích từ các câu trả lời khác cho câu hỏi này.


Bạn đã tìm thấy điều này là một giải pháp hoàn chỉnh? Vì vậy, nhiều câu trả lời khác rất phức tạp, cố gắng giải thích cho rất nhiều trường hợp cạnh. Tôi muốn điều này là sự thật, nó thật đơn giản và thanh lịch.
Le Mot Juated

Tôi chưa bao giờ có vấn đề với nó. Nếu bạn không làm gì bất thường với ngăn xếp điều hướng của mình thì điều này sẽ hoạt động, nếu không, một số giải pháp khác xử lý các trường hợp phức tạp hơn.
Stakenborg

3

Giải pháp thay thế Swift:

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

    return topController!
}

3

Giải pháp này là đầy đủ nhất. Cần xem xét: UINavestionContaptor UIPageViewContaptor UITabBarControll Và trình điều khiển xem được trình bày trên cùng từ trình điều khiển xem trên cùng

Ví dụ là trong Swift 3.

Có 3 quá tải

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

Một giải pháp ngắn gọn nhưng toàn diện trong Swift 4.2, có tính đến UINavlationControllers , UITabBarControllers , trình bày và các trình điều khiển xem con :

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

Sử dụng:

let viewController = UIApplication.shared.topmostViewController()

2

Giải pháp tuyệt vời trong Swift, triển khai trong AppDelegate

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

Tôi nghĩ rằng hầu hết các câu trả lời đã hoàn toàn bỏ qua UINavigationViewController, vì vậy tôi đã xử lý trường hợp sử dụng này bằng cách thực hiện sau đây.

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

Tôi biết nó rất muộn và có thể là dư thừa. Nhưng sau đây là đoạn trích tôi nghĩ ra đang làm việc cho tôi:

    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

Điều này hoạt động rất tốt cho việc tìm kiếm viewContaptor 1 hàng đầu từ bất kỳ trình điều khiển xem gốc nào

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

Không chắc điều này có giúp ích gì cho những gì bạn đang cố gắng thực hiện hay không bằng cách tìm trình điều khiển chế độ xem trên cùng, nhưng tôi đã cố gắng trình bày trình điều khiển chế độ xem mới, nhưng nếu trình điều khiển chế độ xem gốc của tôi đã có hộp thoại phương thức, thì tôi sẽ bị chặn sẽ chuyển sang đầu của tất cả các bộ điều khiển chế độ xem bằng cách sử dụng mã này:

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

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

0

bạn có thể tìm thấy bộ điều khiển xem nhiều nhất bằng cách sử dụng

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

Ngoại trừ điều đó, nếu bạn thực sự đọc câu hỏi, selfkhông có navigationControllertài sản.
Hot Licks

0

Một giải pháp khác dựa trên chuỗi phản hồi, có thể có hoặc không hoạt động tùy thuộc vào phản hồi đầu tiên là gì:

  1. Nhận phản hồi đầu tiên .
  2. Nhận UIViewControll liên quan đến phản hồi đầu tiên đó .

Mã giả ví dụ:

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

Nhanh:

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

Sử dụng:

 if let topController = window.visibleViewController() {
            println(topController)
        }
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.