Nhận UIViewControll hiển thị hiện tại trên màn hình trong AppDelegate.m


126

Dòng điện UIViewControllertrên màn hình cần phản hồi thông báo đẩy từ APN, bằng cách đặt một số chế độ xem huy hiệu. Nhưng làm thế nào tôi có thể có được UIViewControllerphương pháp application:didReceiveRemoteNotification: của AppDelegate.m?

Tôi đã thử sử dụng self.window.rootViewControllerđể có được màn hình hiện tại UIViewController, nó có thể là một UINavigationViewControllerhoặc một số loại điều khiển xem khác. Và tôi phát hiện ra rằng visibleViewControllertài sản của UINavigationViewControllercó thể được sử dụng để có được UIViewControllertrên màn hình. Nhưng tôi có thể làm gì nếu nó không phải là một UINavigationViewController?

Bất kỳ trợ giúp được đánh giá cao! Các mã liên quan như sau.

AppDelegate.m

...
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    //I would like to find out which view controller is on the screen here.

    UIViewController *vc = [(UINavigationViewController *)self.window.rootViewController visibleViewController];
    [vc performSelector:@selector(handleThePushNotification:) withObject:userInfo];
}
...

ViewControllA.m

- (void)handleThePushNotification:(NSDictionary *)userInfo{

    //set some badge view here

}

Câu trả lời:


99

Bạn cũng có thể sử dụng rootViewControllerkhi bộ điều khiển của bạn không phải là UINavigationController:

UIViewController *vc = self.window.rootViewController;

Khi bạn biết trình điều khiển xem gốc, thì nó phụ thuộc vào cách bạn đã xây dựng giao diện người dùng của mình, nhưng bạn có thể tìm ra cách điều hướng qua hệ thống phân cấp của bộ điều khiển.

Nếu bạn cung cấp thêm một số chi tiết về cách bạn xác định ứng dụng của mình, thì tôi có thể cung cấp thêm một số gợi ý.

BIÊN TẬP:

Nếu bạn muốn chế độ xem trên cùng (không phải trình điều khiển xem), bạn có thể kiểm tra

[[[[UIApplication sharedApplication] keyWindow] subviews] lastObject];

mặc dù quan điểm này có thể là vô hình hoặc thậm chí được bao phủ bởi một số cuộc phỏng vấn của nó ...

một lần nữa, nó phụ thuộc vào giao diện người dùng của bạn, nhưng điều này có thể giúp ...


19
Vấn đề với điều này là nếu chế độ xem hiển thị không thuộc về bộ điều khiển chế độ xem gốc (trong trường hợp chế độ xem phương thức và như vậy).
Dima

Vâng tôi đồng ý. Nhưng nó có thể là một UITabViewControll. Không có phương pháp trực tiếp nào để đưa UIViewContoder trên màn hình?
lu nhân dân tệ

2
tốt, bạn thấy đấy, UINavestionControll cung cấp một cách để bạn biết bộ điều khiển nào là trên cùng; bộ điều khiển gốc của bạn sẽ cung cấp thông tin tương tự theo một cách nào đó. Nói chung, nó không thể được suy luận bởi vì nó phụ thuộc hoàn toàn vào cách bạn xây dựng giao diện người dùng của mình và không có hệ thống phân cấp bộ điều khiển rõ ràng (giống như nó xảy ra cho các khung nhìn). Bạn có thể chỉ cần thêm một thuộc tính vào bộ điều khiển gốc và đặt giá trị của nó bất cứ khi nào bạn "đẩy" bộ điều khiển mới lên trên.
sergio

1
Miễn là giá trị được cập nhật, đó dường như là một cách tốt để đi với tôi.
Dima

4
Không có cách nào trực tiếp đến bộ điều khiển từ một UIViewthể hiện. rootViewControllerkhông nhất thiết phải là bộ điều khiển hiện được hiển thị. Nó chỉ ở trên cùng của hệ thống phân cấp xem.
Gingi

101

Tôi luôn thích các giải pháp liên quan đến các danh mục vì chúng được sử dụng và có thể dễ dàng sử dụng lại.

Vì vậy, tôi đã tạo ra một thể loại trên UIWindow. Bây giờ bạn có thể gọi viewerViewContaptor trên UIWindow và điều này sẽ giúp bạn có được bộ điều khiển xem hiển thị bằng cách tìm kiếm phân cấp bộ điều khiển. Điều này hoạt động nếu bạn đang sử dụng điều hướng và / hoặc bộ điều khiển thanh tab. Nếu bạn có một loại điều khiển khác để đề xuất xin vui lòng cho tôi biết và tôi có thể thêm nó.

UIWindow + PazLabs.h (tệp tiêu đề)

#import <UIKit/UIKit.h>

@interface UIWindow (PazLabs)

- (UIViewController *) visibleViewController;

@end

UIWindow + PazLabs.m (tệp thực hiện)

#import "UIWindow+PazLabs.h"

@implementation UIWindow (PazLabs)

- (UIViewController *)visibleViewController {
    UIViewController *rootViewController = self.rootViewController;
    return [UIWindow getVisibleViewControllerFrom:rootViewController];
}

+ (UIViewController *) getVisibleViewControllerFrom:(UIViewController *) vc {
    if ([vc isKindOfClass:[UINavigationController class]]) {
        return [UIWindow getVisibleViewControllerFrom:[((UINavigationController *) vc) visibleViewController]];
    } else if ([vc isKindOfClass:[UITabBarController class]]) {
        return [UIWindow getVisibleViewControllerFrom:[((UITabBarController *) vc) selectedViewController]];
    } else {
        if (vc.presentedViewController) {
            return [UIWindow getVisibleViewControllerFrom:vc.presentedViewController];
        } else {
            return vc;
        }
    }
}

@end

Phiên bản Swift

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

2
Làm thế nào tôi có thể sử dụng điều này cho phiên bản nhanh chóng?
Vijay Singh Rana

2
Tôi không thể hiểu câu hỏi của bạn. Sao chép và dán bên trong mã của bạn.
zirinisp

Điều gì về VC container tùy chỉnh?
Mingming

@Mingming không khó để thêm phần bổ sung nếu kiểm tra xem VC chứa tùy chỉnh của nó (trong phương thức getVisibielContoder) và nếu trả về bộ điều khiển "hiển thị", thường sẽ là vc.childControllers.lastObject cho hầu hết tùy chỉnh triển khai VC của container (tôi cho là), nhưng sẽ phụ thuộc vào cách triển khai của nó.
gadu

1
Tôi vừa đăng một câu trả lời với cách tiếp cận tương tự như trong câu trả lời này ngoại trừ một cú pháp được cập nhật: Đó là sử dụng trường hợp chuyển đổi và tuân theo các quy ước đặt tên Swift 3: stackoverflow.com/a/42486823/3451975
Jeehut 27/2/2017

43

Tiện ích mở rộng đơn giản cho ứng dụng UIApplication trong Swift (quan tâm thậm chí nhiều hơn về phần mềm điều khiển trong UITabBarControlleriPhone) :

extension UIApplication {
    class func topViewController(base: UIViewController? = UIApplication.sharedApplication().keyWindow?.rootViewController) -> UIViewController? {

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

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

            if let top = moreNavigationController.topViewController where top.view.window != nil {
                return topViewController(top)
            } else if let selected = tab.selectedViewController {
                return topViewController(selected)
            }
        }

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

        return base
    }
}

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

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

Hoạt động hoàn hảo :-)

CẬP NHẬT cho mã sạch:

extension UIViewController {
    var top: UIViewController? {
        if let controller = self as? UINavigationController {
            return controller.topViewController?.top
        }
        if let controller = self as? UISplitViewController {
            return controller.viewControllers.last?.top
        }
        if let controller = self as? UITabBarController {
            return controller.selectedViewController?.top
        }
        if let controller = presentedViewController {
            return controller.top
        }
        return self
    }
}

1
Đây dường như là mã cho Swift 2.x. Swift 3.x không còn "đâu" nữa. Ngoài ra, "sharedApplication ()" hiện được "chia sẻ". Không phải là một thỏa thuận lớn. Chỉ mất một phút để cập nhật. Có thể là tốt để đề cập rằng nó sử dụng đệ quy. Ngoài ra, mỗi lệnh gọi đến topViewControll cần có tiền tố "cơ sở:".
Jeff Muir

37

Bạn cũng có thể đăng thông báo qua NSNotificationCenter. Điều này cho phép bạn xử lý một số tình huống trong đó vượt qua hệ thống phân cấp của trình điều khiển chế độ xem có thể khó khăn - ví dụ: khi các phương thức được trình bày, v.v.

Ví dụ,

// MyAppDelegate.h
NSString * const UIApplicationDidReceiveRemoteNotification;

// MyAppDelegate.m
NSString * const UIApplicationDidReceiveRemoteNotification = @"UIApplicationDidReceiveRemoteNotification";

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {

    [[NSNotificationCenter defaultCenter]
     postNotificationName:UIApplicationDidReceiveRemoteNotification
     object:self
     userInfo:userInfo];
}

Trong mỗi Bộ điều khiển xem của bạn:

-(void)viewDidLoad {
    [[NSNotificationCenter defaultCenter] 
      addObserver:self
      selector:@selector(didReceiveRemoteNotification:)                                                  
      name:UIApplicationDidReceiveRemoteNotification
      object:nil];
}

-(void)viewDidUnload {
    [[NSNotificationCenter defaultCenter] 
      removeObserver:self
      name:UIApplicationDidReceiveRemoteNotification
      object:nil];
}

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
    // see http://stackoverflow.com/a/2777460/305149
   if (self.isViewLoaded && self.view.window) {
      // handle the notification
   }
}

Bạn cũng có thể sử dụng phương pháp này cho các điều khiển công cụ cần cập nhật khi nhận được thông báo và được sử dụng bởi một số bộ điều khiển xem. Trong trường hợp đó, xử lý các cuộc gọi add / remove của người quan sát trong các phương thức init và dealloc tương ứng.


1
Có gì addObserver:barbên trong viewDidLoad? Tôi có phải thay thế bằng self?
CainaSouza

Cảm ơn đã chỉ ra rằng - nó nên là chính mình. Tôi sẽ cập nhật câu trả lời.
Aneil Mallavarapu

sự cố trong khi nhận tất cả các khóa từ userInfo .. Có ý kiến ​​gì không? [NSConcittleNotification allKeys]: bộ chọn không được nhận dạng được gửi đến phiên bản 0x1fd87480 2013-07-05 16: 10: 36.469 Providence [2961: 907] *** Chấm dứt ứng dụng do ngoại lệ chưa được phát hiện 'NSInvalidArgumentException', lý do: ' bộ chọn gửi đến ví dụ 0x1fd87480 '
Awais Tariq

@AwaisTariq - Hmmm - tôi đoán là đối tượng được iOS chuyển sang didReceiveRemoteNotification không thực sự là một NSDipedia, như giao diện chỉ định.
Aneil Mallavarapu

Điều gì xảy ra nếu người dùng chưa điều hướng đến lớp người quan sát của bạn? : /
halbano

15

Đây là một cách tiếp cận sử dụng cú pháp trường hợp chuyển đổi tuyệt vời trong Swift 3/4/5 :

extension UIWindow {
    /// Returns the currently visible view controller if any reachable within the window.
    public var visibleViewController: UIViewController? {
        return UIWindow.visibleViewController(from: rootViewController)
    }

    /// Recursively follows navigation controllers, tab bar controllers and modal presented view controllers starting
    /// from the given view controller to find the currently visible view controller.
    ///
    /// - Parameters:
    ///   - viewController: The view controller to start the recursive search from.
    /// - Returns: The view controller that is most probably visible on screen right now.
    public static func visibleViewController(from viewController: UIViewController?) -> UIViewController? {
        switch viewController {
        case let navigationController as UINavigationController:
            return UIWindow.visibleViewController(from: navigationController.visibleViewController ?? navigationController.topViewController)

        case let tabBarController as UITabBarController:
            return UIWindow.visibleViewController(from: tabBarController.selectedViewController)

        case let presentingViewController where viewController?.presentedViewController != nil:
            return UIWindow.visibleViewController(from: presentingViewController?.presentedViewController)

        default:
            return viewController
        }
    }
}

Ý tưởng cơ bản giống như trong câu trả lời của zirinisp, đó chỉ là sử dụng cú pháp giống Swift 3+ hơn.


Sử dụng

Bạn có thể muốn tạo một tập tin có tên UIWindowExtension.swift. Hãy chắc chắn rằng nó bao gồm import UIKitcâu lệnh, bây giờ sao chép mã mở rộng ở trên .

Về phía cuộc gọi, nó có thể được sử dụng mà không cần bất kỳ bộ điều khiển xem cụ thể nào :

if let visibleViewCtrl = UIApplication.shared.keyWindow?.visibleViewController {
    // do whatever you want with your `visibleViewCtrl`
}

Hoặc nếu bạn biết bộ điều khiển chế độ xem hiển thị của bạn có thể truy cập được từ bộ điều khiển chế độ xem cụ thể :

if let visibleViewCtrl = UIWindow.visibleViewController(from: specificViewCtrl) {
    // do whatever you want with your `visibleViewCtrl`
}

Tôi hy vọng nó sẽ giúp!


Trường hợp thứ ba sẽ sụp đổ vì một đệ quy vô hạn. Cách khắc phục là đổi tên vc as presentingViewControllervà truyền presentingViewController.presentedViewControllertham số cho phương thức đệ quy.
Ikhsan Assaat

Tôi đã không nhận được nó, xin lỗi. Ý bạn là UIWindow.visibleViewController(from: presentedViewController)thay vào đó UIWindow.visibleViewController(from: presentingViewController.presentedViewController)?
Jeehut

đúng, presentedViewControllerviewControllerlà cùng một đối tượng và nó sẽ tự gọi phương thức cho đến khi ngăn xếp tràn ra (ý định chơi chữ). Vì vậy, nó sẽ là case let presentingViewController where viewController?.presentedViewController != nil: return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
Ikhsan Assaat

1
Giải pháp này hiệu quả khi những người khác thì không. Bạn nên cập nhật lên Swift 5. Về cơ bản không có thay đổi. Chỉ cần cập nhật tiêu đề cho câu trả lời của bạn.
TM Lynch

14

Tôi đã thấy rằng iOS 8 đã làm hỏng mọi thứ. Trong iOS 7 có một cái mới UITransitionViewvề phân cấp chế độ xem bất cứ khi nào bạn có một bản trình bày vừa phải UINavigationController. Dù sao, đây là mã của tôi tìm được VC cao nhất. Gọi getTopMostViewControllersẽ trả lại một VC mà bạn sẽ có thể gửi tin nhắn như thế nào presentViewController:animated:completion. Mục đích của nó là để cung cấp cho bạn một VC mà bạn có thể sử dụng để trình bày một VC phương thức, do đó rất có thể nó sẽ dừng và quay trở lại tại các lớp container như UINavigationControllervà KHÔNG phải là VC chứa trong chúng. Không nên khó để điều chỉnh mã để làm điều đó. Tôi đã kiểm tra mã này trong các tình huống khác nhau trong iOS 6, 7 và 8. Vui lòng cho tôi biết nếu bạn tìm thấy lỗi.

+ (UIViewController*) getTopMostViewController
{
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    if (window.windowLevel != UIWindowLevelNormal) {
        NSArray *windows = [[UIApplication sharedApplication] windows];
        for(window in windows) {
            if (window.windowLevel == UIWindowLevelNormal) {
                break;
            }
        }
    }

    for (UIView *subView in [window subviews])
    {
        UIResponder *responder = [subView nextResponder];

        //added this block of code for iOS 8 which puts a UITransitionView in between the UIWindow and the UILayoutContainerView
        if ([responder isEqual:window])
        {
            //this is a UITransitionView
            if ([[subView subviews] count])
            {
                UIView *subSubView = [subView subviews][0]; //this should be the UILayoutContainerView
                responder = [subSubView nextResponder];
            }
        }

        if([responder isKindOfClass:[UIViewController class]]) {
            return [self topViewController: (UIViewController *) responder];
        }
    }

    return nil;
}

+ (UIViewController *) topViewController: (UIViewController *) controller
{
    BOOL isPresenting = NO;
    do {
        // this path is called only on iOS 6+, so -presentedViewController is fine here.
        UIViewController *presented = [controller presentedViewController];
        isPresenting = presented != nil;
        if(presented != nil) {
            controller = presented;
        }

    } while (isPresenting);

    return controller;
}

Vui lòng không trùng lặp câu trả lời - đánh dấu các câu hỏi là trùng lặp nếu có hoặc trả lời các câu hỏi riêng lẻ với câu trả lời cụ thể mà chúng xứng đáng nếu chúng không trùng lặp.
Flexo

13

Cách ít mã hơn tất cả các giải pháp khác:

Phiên bản Objective-C:

- (UIViewController *)getTopViewController {
    UIViewController *topViewController = [[[[UIApplication sharedApplication] delegate] window] rootViewController];
    while (topViewController.presentedViewController) topViewController = topViewController.presentedViewController;

    return topViewController;
}

Phiên bản Swift 2.0: (tín dụng cho Steve.B)

func getTopViewController() -> UIViewController {
    var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil) {
        topViewController = topViewController.presentedViewController!
    }
    return topViewController
}

Hoạt động ở bất cứ đâu trong ứng dụng của bạn, ngay cả với phương thức.


1
Điều này không xử lý tình huống trong đó trình điều khiển khung nhìn được trình bày là một trình điều khiển UINavigationControllercó con riêng của nó.
levigroker

@levigroker, có lẽ đó là cách bạn kiến ​​trúc quan điểm của mình? Nó hoạt động tốt đối với tôi để sử dụng điều này với một Nav. (đó là cách tôi đang sử dụng nó)
jungledev 6/12/2016

@jungledev Tôi chắc chắn bạn đã đúng. Điều đó nói rằng, một giải pháp hoạt động trong tất cả các cấu hình bộ điều khiển xem là những gì cần thiết.
levigroker

@levigroker nó không làm việc trong tất cả vc chuẩn configurations- tôi làm việc với ứng dụng trên có một kiến trúc thực sự phức tạp, được sử dụng bởi hơn 500k người dùng, và các công trình này ở khắp mọi nơi trong ứng dụng. Có lẽ bạn nên đăng câu hỏi tại sao nó không hoạt động trong chế độ xem của bạn, với các ví dụ mã?
jungledev

jungledev Tôi rất vui khi mã này hoạt động cho bạn, nhưng nó dường như không phải là một giải pháp hoàn chỉnh. Câu trả lời của @ zirinisp hoạt động hoàn hảo trong tình huống của tôi.
levigroker 6/12/2016

8

Câu trả lời của zirinisp trong Swift:

extension UIWindow {

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

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

        if vc.isKindOfClass(UINavigationController.self) {

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

        } else if vc.isKindOfClass(UITabBarController.self) {

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

        } else {

            if let presentedViewController = vc.presentedViewController {

                return UIWindow.getVisibleViewControllerFrom(presentedViewController.presentedViewController!)

            } else {

                return vc;
            }
        }
    }
}

Sử dụng:

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

Đó là as!navigationController.visibleViewController!cho Swift 2.0
LinusGeffarth

7

Chỉ định tiêu đề cho mỗi ViewContoder và sau đó lấy tiêu đề của ViewContoder hiện tại theo mã được đưa ra dưới đây.

-(void)viewDidUnload {
  NSString *currentController = self.navigationController.visibleViewController.title;

Sau đó kiểm tra nó bằng tiêu đề của bạn như thế này

  if([currentController isEqualToString:@"myViewControllerTitle"]){
    //write your code according to View controller.
  }
}

Đây là câu trả lời hay nhất, bạn cũng có thể đặt tên cho viewContoder của mình bằng:self.title = myPhotoView
Resty

5

Tôi là tốt hơn! :)

extension UIApplication {
    var visibleViewController : UIViewController? {
        return keyWindow?.rootViewController?.topViewController
    }
}

extension UIViewController {
    fileprivate var topViewController: UIViewController {
        switch self {
        case is UINavigationController:
            return (self as! UINavigationController).visibleViewController?.topViewController ?? self
        case is UITabBarController:
            return (self as! UITabBarController).selectedViewController?.topViewController ?? self
        default:
            return presentedViewController?.topViewController ?? self
        }
    }
}

4

Tại sao không chỉ xử lý mã thông báo đẩy trong đại biểu ứng dụng? Có liên quan trực tiếp đến một quan điểm?

Bạn có thể kiểm tra xem chế độ xem của UIViewContoder hiện có hiển thị hay không bằng cách kiểm tra xem thuộc windowtính của chế độ xem đó có giá trị hay không. Xem thêm tại đây .


Vâng, nó có liên quan đến một chế độ xem, vì tôi phải hiển thị chế độ xem huy hiệu. Hãy để tôi kiểm tra các liên kết. cảm ơn bạn :)
lu nhân dân tệ

4

Chỉ cần thêm vào câu trả lời @zirinisp.

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

}

Cảm ơn @zirinisp.


3

Về NSNotificationCenter Đăng ở trên (xin lỗi không thể tìm ra nơi để gửi bình luận theo nó ...)

Trong trường hợp một số người nhận được lỗi - [NSConcittleNotification allKeys]. Thay đổi điều này:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo

đến đây:

-(void)didReceiveRemoteNotification:(NSNotification*)notif {
NSDictionary *dict = notif.userInfo;
}

3

Điều này làm việc cho tôi. Tôi có nhiều mục tiêu có các bộ điều khiển khác nhau nên các câu trả lời trước đó dường như không hoạt động.

đầu tiên bạn muốn điều này trong lớp AppDelegate của bạn:

var window: UIWindow?

sau đó, trong chức năng của bạn

let navigationController = window?.rootViewController as? UINavigationController
if let activeController = navigationController!.visibleViewController {
    if activeController.isKindOfClass( MyViewController )  {
        println("I have found my controller!")    
   }
}

2

Đây là cách tốt nhất có thể mà tôi đã thử. Nếu nó giúp được ai ...

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

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

    return topController;
}

2
extension UIApplication {
    /// The top most view controller
    static var topMostViewController: UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
    }
}

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else {
            return self
        }
    }
}

Với điều này, bạn có thể dễ dàng có được trình điều khiển xem bài đăng hàng đầu như vậy

let viewController = UIApplication.topMostViewController

Một điều cần lưu ý là nếu có một UIAlertControll hiện đang được hiển thị, UIApplication.topMostViewControllersẽ trả về a UIAlertController.


1

Phiên bản Swift 2.0 của câu trả lời của jungledev

func getTopViewController() -> UIViewController {
    var topViewController = UIApplication.sharedApplication().delegate!.window!!.rootViewController!
    while (topViewController.presentedViewController != nil) {
        topViewController = topViewController.presentedViewController!
    }
    return topViewController
}

1

Tôi đã tạo một danh mục cho UIApplicationvới visibleViewControllerstài sản. Ý tưởng chính là khá đơn giản. Tôi quẹt viewDidAppearviewDidDisappearphương pháp trong UIViewController. Trong viewDidAppearphương thức viewControll được thêm vào stack. Trong viewDidDisappearphương thức viewControll được gỡ bỏ khỏi stack. NSPointerArrayđược sử dụng thay vì NSArrayđể lưu trữ UIViewControllercác tài liệu tham khảo yếu . Cách tiếp cận này hoạt động cho bất kỳ hệ thống phân cấp viewControllers.

Ứng dụng + VisibleViewControllers.h

#import <UIKit/UIKit.h>

@interface UIApplication (VisibleViewControllers)

@property (nonatomic, readonly) NSArray<__kindof UIViewController *> *visibleViewControllers;

@end

Ứng dụng + VisibleViewControllers.m

#import "UIApplication+VisibleViewControllers.h"
#import <objc/runtime.h>

@interface UIApplication ()

@property (nonatomic, readonly) NSPointerArray *visibleViewControllersPointers;

@end

@implementation UIApplication (VisibleViewControllers)

- (NSArray<__kindof UIViewController *> *)visibleViewControllers {
    return self.visibleViewControllersPointers.allObjects;
}

- (NSPointerArray *)visibleViewControllersPointers {
    NSPointerArray *pointers = objc_getAssociatedObject(self, @selector(visibleViewControllersPointers));
    if (!pointers) {
        pointers = [NSPointerArray weakObjectsPointerArray];
        objc_setAssociatedObject(self, @selector(visibleViewControllersPointers), pointers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return pointers;
}

@end

@implementation UIViewController (UIApplication_VisibleViewControllers)

+ (void)swizzleMethodWithOriginalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
    Method originalMethod = class_getInstanceMethod(self, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(self, swizzledSelector);
    BOOL didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self swizzleMethodWithOriginalSelector:@selector(viewDidAppear:)
                               swizzledSelector:@selector(uiapplication_visibleviewcontrollers_viewDidAppear:)];
        [self swizzleMethodWithOriginalSelector:@selector(viewDidDisappear:)
                               swizzledSelector:@selector(uiapplication_visibleviewcontrollers_viewDidDisappear:)];
    });
}

- (void)uiapplication_visibleviewcontrollers_viewDidAppear:(BOOL)animated {
    [[UIApplication sharedApplication].visibleViewControllersPointers addPointer:(__bridge void * _Nullable)self];
    [self uiapplication_visibleviewcontrollers_viewDidAppear:animated];
}

- (void)uiapplication_visibleviewcontrollers_viewDidDisappear:(BOOL)animated {
    NSPointerArray *pointers = [UIApplication sharedApplication].visibleViewControllersPointers;
    for (int i = 0; i < pointers.count; i++) {
        UIViewController *viewController = [pointers pointerAtIndex:i];
        if ([viewController isEqual:self]) {
            [pointers removePointerAtIndex:i];
            break;
        }
    }
    [self uiapplication_visibleviewcontrollers_viewDidDisappear:animated];
}

@end

https://gist.github.com/medvedzzz/e6287b99011f2437ac0beb5a72a897f0

Phiên bản Swift 3

Ứng dụng + VisibleViewControllers.swift

import UIKit

extension UIApplication {

    private struct AssociatedObjectsKeys {
        static var visibleViewControllersPointers = "UIApplication_visibleViewControllersPointers"
    }

    fileprivate var visibleViewControllersPointers: NSPointerArray {
        var pointers = objc_getAssociatedObject(self, &AssociatedObjectsKeys.visibleViewControllersPointers) as! NSPointerArray?
        if (pointers == nil) {
            pointers = NSPointerArray.weakObjects()
            objc_setAssociatedObject(self, &AssociatedObjectsKeys.visibleViewControllersPointers, pointers, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
        return pointers!
    }

    var visibleViewControllers: [UIViewController] {
        return visibleViewControllersPointers.allObjects as! [UIViewController]
    }
}

extension UIViewController {

    private static func swizzleFunc(withOriginalSelector originalSelector: Selector, swizzledSelector: Selector) {
        let originalMethod = class_getInstanceMethod(self, originalSelector)
        let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)
        let didAddMethod = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
        if didAddMethod {
            class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    }

    override open class func initialize() {
        if self != UIViewController.self {
            return
        }
        let swizzlingClosure: () = {
            UIViewController.swizzleFunc(withOriginalSelector: #selector(UIViewController.viewDidAppear(_:)),
                                         swizzledSelector: #selector(uiapplication_visibleviewcontrollers_viewDidAppear(_:)))
            UIViewController.swizzleFunc(withOriginalSelector: #selector(UIViewController.viewDidDisappear(_:)),
                                         swizzledSelector: #selector(uiapplication_visibleviewcontrollers_viewDidDisappear(_:)))
        }()
        swizzlingClosure
    }

    @objc private func uiapplication_visibleviewcontrollers_viewDidAppear(_ animated: Bool) {
        UIApplication.shared.visibleViewControllersPointers.addPointer(Unmanaged.passUnretained(self).toOpaque())
        uiapplication_visibleviewcontrollers_viewDidAppear(animated)
    }

    @objc private func uiapplication_visibleviewcontrollers_viewDidDisappear(_ animated: Bool) {
        let pointers = UIApplication.shared.visibleViewControllersPointers
        for i in 0..<pointers.count {
            if let pointer = pointers.pointer(at: i) {
                let viewController = Unmanaged<AnyObject>.fromOpaque(pointer).takeUnretainedValue() as? UIViewController
                if viewController.isEqual(self) {
                    pointers.removePointer(at: i)
                    break
                }
            }
        }
        uiapplication_visibleviewcontrollers_viewDidDisappear(animated)
    }
}

https://gist.github.com/medvedzzz/ee6f4071639d987793977dba04e11399


1

Luôn kiểm tra cấu hình bản dựng của bạn nếu bạn đang chạy ứng dụng của mình với gỡ lỗi hoặc phát hành.

LƯU Ý QUAN TRỌNG: Bạn không thể kiểm tra nó mà không chạy ứng dụng của mình ở chế độ gỡ lỗi

Đây là giải pháp của tôi

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.