Geçerli görüntülenen UIViewController'ı AppDelegate.m'de ekranda alın.


126

Ekrandaki akımın UIViewController, bazı rozet görünümleri ayarlayarak APN'lerden gelen push bildirimlerine yanıt vermesi gerekir. Ama UIViewControllergiriş yöntemini nasıl elde edebilirim application:didReceiveRemoteNotification:AppDelegate.m ?

self.window.rootViewControllerMevcut görüntülemeyi elde etmek için kullanmayı denedim UIViewController, bir UINavigationViewControllerveya başka tür bir görünüm denetleyicisi olabilir. Ve visibleViewControllerözelliğinin ekrana UINavigationViewControllerulaşmak için kullanılabileceğini öğrendim UIViewController. Ama a değilse ne yapabilirim UINavigationViewController?

Herhangi bir yardım takdir edilmektedir! İlgili kod aşağıdaki gibidir.

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

ViewControllerA.m

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

    //set some badge view here

}

Yanıtlar:


99

rootViewControllerDenetleyiciniz aşağıdaki durumlarda da kullanabilirsiniz UINavigationController:

UIViewController *vc = self.window.rootViewController;

Kök görünüm denetleyicisini öğrendikten sonra, kullanıcı arabiriminizi nasıl oluşturduğunuza bağlıdır, ancak denetleyiciler hiyerarşisinde gezinmenin bir yolunu muhtemelen bulabilirsiniz.

Uygulamanızı tanımlama şeklinizle ilgili biraz daha ayrıntı verirseniz, biraz daha ipucu verebilirim.

DÜZENLE:

En üstteki görünümü (görüntü denetleyicisini değil) istiyorsanız, kontrol edebilirsiniz.

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

bu görüş görünmez olabilir veya hatta bazı alt görünümleri tarafından kapsanabilir ...

yine, kullanıcı arayüzünüze bağlıdır, ancak bu yardımcı olabilir ...


19
Bununla ilgili sorun, görünür görünümün kök görünüm denetleyicisine ait olmamasıdır (modal görünümler ve benzeri durumlarda).
Dima

Evet ediyorum. Ama belki bir UITabViewController. UIViewController'ı ekrana getirmek için doğrudan bir yöntem yok mu?
lu yuan

2
görüyorsunuz, UINavigationController hangi kontrolcünün en üstte olduğunu bilmeniz için bir yol sağlar; kök denetleyiciniz bir şekilde aynı bilgileri sağlamalıdır. Genel olarak çıkarılamaz çünkü kesinlikle kullanıcı arayüzünüzü nasıl oluşturduğunuza bağlıdır ve açık bir denetleyici hiyerarşisi yoktur (görünümler için olduğu gibi). Kök denetleyicinize bir özellik ekleyebilir ve en üstte yeni bir denetleyiciyi her "ittiğinizde" değerini ayarlayabilirsiniz.
sergio

1
Değer güncel tutulduğu sürece, bu benim için de iyi bir yol gibi görünüyor.
Dima

4
Bir UIViewörnekten denetleyiciye ulaşmanın doğrudan bir yolu yoktur . rootViewControllerolduğu değil mutlaka anda gösterilen kontrolör. Yalnızca görünüm hiyerarşisinin en üstünde.
Gingi

101

Her zaman, cıvatalı oldukları ve kolayca yeniden kullanılabilecekleri kategorileri içeren çözümleri seviyorum.

Bu yüzden UIWindow'da bir kategori oluşturdum. Artık UIWindow'da visibleViewController'ı çağırabilirsiniz ve bu, denetleyici hiyerarşisini araştırarak size görünür görünüm denetleyicisini getirir. Bu, gezinme ve / veya sekme çubuğu denetleyicisi kullanıyorsanız çalışır. Önermek için başka türde bir denetleyiciniz varsa lütfen bize bildirin, ekleyebilirim.

UIWindow + PazLabs.h (başlık dosyası)

#import <UIKit/UIKit.h>

@interface UIWindow (PazLabs)

- (UIViewController *) visibleViewController;

@end

UIWindow + PazLabs.m (uygulama dosyası)

#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

Swift Versiyonu

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
bunu hızlı sürüm için nasıl kullanabilirim?
Vijay Singh Rana

2
Sorunuzu anlayamıyorum. Kodunuzun içine kopyalayıp yapıştırın.
zirinisp

Özel kapsayıcı VC ne olacak?
Mingming

@Mingming, özel kapsayıcı VC'sinin (getVisibielController yönteminde) olup olmadığını kontrol etmek ve öyleyse "görünür" denetleyiciyi döndürmek için fazladan bir ekleme yapmak o kadar zor olmamalı, bu genellikle çoğu özel için vc.childControllers.lastObject olacaktır kapsayıcı VC uygulamaları (sanırım), ancak nasıl uygulandığına bağlı olacaktır.
gadu

1
Güncellenmiş sözdizimi dışında bu yanıtla aynı yaklaşıma sahip bir yanıt gönderdim: Bir anahtar durumu kullanıyor ve Swift 3 adlandırma kurallarını izliyor: stackoverflow.com/a/42486823/3451975
Jeehut

43

Swift'de UIApplicationUITabBarController için basit uzantı ( iPhone'daki daha fazlaNavigationController'ı bile önemsiyor ) :

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

Basit kullanım:

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

Mükemmel çalışıyor :-)

Temiz kod için GÜNCELLEME:

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
Bu Swift 2.x için bir kod gibi görünüyor. Swift 3.x artık "nerede" ifadesine sahip değil. Ayrıca, "sharedApplication ()" artık "paylaşılıyor". Önemli bir şey değil. Güncelleme sadece bir dakika sürer. Özyineleme kullandığından bahsetmek iyi olabilir. Ayrıca, topViewController'a yapılan her çağrı "base:" önekine ihtiyaç duymalıdır.
Jeff Muir

37

NSNotificationCenter aracılığıyla da bir bildirim gönderebilirsiniz. Bu, görünüm denetleyicisi hiyerarşisini geçmenin zor olabileceği birkaç durumu ele almanızı sağlar - örneğin, modlar sunulurken vb.

Örneğin,

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

Görünüm Denetleyicilerinizin her birinde:

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

Bu yaklaşımı, bir bildirim alındığında güncellenmesi gereken ve birkaç görünüm denetleyicisi tarafından kullanılan alet kontrolleri için de kullanabilirsiniz. Bu durumda, sırasıyla init ve dealloc yöntemlerinde add / remove observer çağrılarını işleyin.


1
addObserver:barİçinde ne var viewDidLoad? İle değiştirmek zorunda selfmıyım?
CainaSouza

Bunu belirttiğin için teşekkürler - kendi kendine olmalı. Cevabı güncelleyeceğim.
Aneil Mallavarapu

userInfo'dan tüm anahtarları alırken çökme .. Herhangi bir fikriniz var mı? [NSConcreteNotification allKeys]: 0x1fd87480 örneğine gönderilen tanınmayan seçici 16: 10: 36.469 Providence [2961: 907] *** Yakalanmamış istisna 'NSInvalidArgumentException' nedeniyle uygulama sonlandırılıyor, neden: '- [NSConcreteNotification allKeys]: tanınmıyor seçici 0x1fd87480 'örneğine gönderildi
Awais Tariq

@AwaisTariq - Hmmm - benim tahminim iOS tarafından didReceiveRemoteNotification'a iletilen nesnenin arayüzün belirttiği gibi aslında bir NSDictionary olmadığıdır.
Aneil Mallavarapu

Ya kullanıcı henüz gözlemci sınıfınıza gitmediyse? : /
halbano

15

kod

İşte harika kullanarak bir yaklaşım switch-case sözdizimi içinde 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
        }
    }
}

Temel fikir, zirinisp'in cevabındaki ile aynı, sadece daha Swift 3+ benzeri bir sözdizimi kullanıyor.


kullanım

Muhtemelen adlı bir dosya oluşturmak istiyorsunuz UIWindowExtension.swift. İfadeyi içerdiğinden emin olun import UIKit, şimdi yukarıdaki uzantı kodunu kopyalayın .

Çağrı tarafında , herhangi bir özel görünüm denetleyicisi olmadan kullanılabilir :

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

Veya görünür görünüm denetleyicinize belirli bir görünüm denetleyicisinden erişilebildiğini biliyorsanız :

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

Umut ediyorum bu yardım eder!


Üçüncü durum, sonsuz özyineleme nedeniyle çökecektir. Düzeltme, vc'yi olarak yeniden adlandırmak ve parametre olarak özyinelemeli yönteme presentingViewControllergeçirmektir presentingViewController.presentedViewController.
Ikhsan Assaat

Pek anlamadım, üzgünüm. Bunun UIWindow.visibleViewController(from: presentedViewController)yerine olması gerektiğini mi söylüyorsunUIWindow.visibleViewController(from: presentingViewController.presentedViewController) mi ?
Jeehut

doğru, presentedViewController ve viewControlleraynı nesnedir ve yığın taşana kadar yöntemi kendisiyle birlikte çağırır (amaçlanan). Öyleyse öyle olacak case let presentingViewController where viewController?.presentedViewController != nil: return UIWindow.visibleViewController(from: presentingViewController.presentedViewController)
İhsan Assaat

1
Bu çözüm, diğerleri çalışmadığında işe yaradı. Swift 5'e güncellemelisiniz. Esasen değişiklik yok. Cevabınız için başlığı güncellemeniz yeterli.
TM Lynch

14

İOS 8'in her şeyi mahvettiğini buldum. İOS 7'de, UITransitionViewmod olarak sunulduğunda görünüm hiyerarşisinde yeni bir şey vardır UINavigationController. Her neyse, işte en üstteki VC'yi bulan kodum. Arama getTopMostViewController, gibi bir mesaj gönderebilmeniz gereken bir VC döndürmelidir presentViewController:animated:completion. Amacı, size modal bir VC sunmak için kullanabileceğiniz bir VC elde etmektir, bu nedenle büyük olasılıkla UINavigationControllerbunların içerdiği VC gibi konteyner sınıflarında duracak ve geri dönecektir . Bunu yapmak için kodu uyarlamak da zor olmamalı. Bu kodu iOS 6, 7 ve 8'de çeşitli durumlarda test ettim. Hata bulursanız lütfen bana bildirin.

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

Lütfen cevapları çoğaltmayın - ya soruları kopya olarak işaretleyin ya da mükerrer değilse, tek tek soruları hak ettikleri belirli cevaplarla cevaplayın.
Flexo

13

Diğer tüm çözümlerden çok daha az kod:

Objective-C sürümü:

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

    return topViewController;
}

Swift 2.0 sürümü: (kredi Steve.B'ye gider)

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

Modallerle bile uygulamanızın herhangi bir yerinde çalışır.


1
Bu, sunulan görünüm denetleyicisinin UINavigationControllerkendi alt öğelerine sahip olduğu durumu işlemez .
levigroker

@levigroker, belki de görüşlerinizi tasarlama yolunuzdur? Bunu bir Nav ile kullanmak benim için iyi çalışıyor. (ben böyle kullanıyorum)
jungledev

@jungledev Eminim haklısınız. Bununla birlikte, gerekli olan tüm görüntü denetleyicisi yapılandırmalarında çalışan bir çözümdür.
levigroker

@levigroker o does tüm standart vc iş gerçekten karmaşık bir mimariye sahiptir üzerine uygulama İşten configurations-, 500k üzerinde kullanıcılar tarafından kullanılan ve uygulamada her yerde bu eserler edilir. Belki de kod örnekleriyle neden sizin görüşünüzde işe yaramadığını soran bir soru göndermelisiniz?
jungledev

jungledev Bu kodun sizin için çalışmasına sevindim, ancak tam bir çözüm gibi görünmüyor. @ zirinisp'in cevabı benim durumumda mükemmel çalışıyor.
levigroker

8

zirinisp'in Swift Cevabı:

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

Kullanımı:

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

Bu var as!ve navigationController.visibleViewController!Swift 2.0 için
LinusGeffarth

7

Her bir ViewController için başlık belirtin ve ardından aşağıda verilen kodla mevcut ViewController'ın başlığını alın.

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

Sonra başlığınıza göre kontrol edin

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

Dfntly en iyi cevap, ayrıca viewController'ınızı şu şekilde adlandırabilirsiniz:self.title = myPhotoView
Resty

5

Benimki daha iyi! :)

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

Neden uygulama temsilcisindeki anlık bildirim kodunu işlemiyorsunuz? Doğrudan bir görüşle mi ilgili?

Bir UIViewController görünümünün şu anda görünür olup olmadığını, görünümünün windowözelliğinin bir değeri olup olmadığını kontrol ederek kontrol edebilirsiniz . Daha fazlasını burada görün .


Evet, rozet görünümünü göstermem gerektiği için bir görünümle ilgili. bağlantıyı kontrol edeyim. teşekkür ederim :)
lu yuan

4

@Zirinisp cevabına ek olarak.

Bir dosya oluşturun, adlandırın UIWindowExtension.swiftve aşağıdaki parçacığı yapıştırın:

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
}

Her yerde şu şekilde kullanın:

if let topVC = getTopViewController() {

}

@Zirinisp'e teşekkürler.


3

Yukarıdaki NSNotificationCenter Gönderi ile ilgili olarak (üzgünüm, altında nerede yorum yazılacağını bulamıyorum ...)

Bazılarının - [NSConcreteNotification allKeys] hatasını alması durumunda. Bunu değiştir:

-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo

buna:

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

3

Bu benim için çalıştı. Farklı denetleyicileri olan birçok hedefim var, bu nedenle önceki yanıtlar işe yaramadı.

öncelikle bunu AppDelegate sınıfınızın içinde istiyorsunuz:

var window: UIWindow?

sonra, işlevinde

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

2

Bu, denediğim mümkün olan en iyi yol. Birine yardımı olacaksa ...

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

Bununla, üst yayın görünümü denetleyicisini kolayca edinebilirsiniz.

let viewController = UIApplication.topMostViewController

Unutulmaması gereken bir nokta, şu anda görüntülenen bir UIAlertController varsa, UIApplication.topMostViewControllerbir UIAlertController.


1

Jungledev'in cevabının hızlı 2.0 versiyonu

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

1

Mülk UIApplicationile için bir kategori oluşturdum visibleViewControllers. Ana fikir oldukça basit. Döndüm viewDidAppearve viewDidDisappearyöntemlerim var UIViewController. Gelen viewDidAppearyöntem viewController yığınına eklenir. Gelen viewDidDisappearyöntem viewController istiften ayrılmaktadır. zayıf depolamak NSPointerArrayyerine kullanılırNSArrayUIViewController referanslarını . Bu yaklaşım, herhangi bir viewControllers hiyerarşisi için çalışır.

UIApplication + VisibleViewControllers.h

#import <UIKit/UIKit.h>

@interface UIApplication (VisibleViewControllers)

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

@end

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

Swift 3 versiyonu

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

Uygulamanızı hata ayıklama veya yayınlama ile çalıştırıyorsanız her zaman derleme yapılandırmanızı kontrol edin.

ÖNEMLİ NOT: Uygulamanızı hata ayıklama modunda çalıştırmadan test edemezsiniz

Bu benim çözümümdü

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.