程序员

记录一次iOS无法获取topViewController的问题

2019-03-07  本文已影响13人  iOS_Yee
问题关键

[[UIApplication sharedApplication].keyWindow rootViewController]
[[[UIApplication sharedApplication] delegate] window].rootViewController

问题描述

目前接手的项目是一个比较老的项目,界面的跳转大多数都是利用获取topViewController的方式。在添加新功能的时候发现topViewController有时不一定是自己需要的,取到错误的情况基本上都是窗口上不止一个UIWindow,基本上分为下面三种情况。

  1. UITextView | UITextField
  2. UIAlertController | UIAlertView | 系统自己的控件
  3. 自定义的maskView(有新的UIWindow创建)
问题定位

界面跳转的时候,因为拿到的getTopViewController有时不是理想ViewController,界面无法跳转,想着可能是方法错了,在网上检索,发现用‘递归’思想获取ViewController没有问题,但是输出的TopViewController一直不正确,通过打印[[UIApplication sharedApplication].keyWindow rootViewController],结果输出不是UITabBarViewController。这才发现问题所在。如果换成[[[UIApplication sharedApplication] delegate] window].rootViewController问题就解决了。

获取topViewController的代码如下。

+ (UIViewController *)getTopViewController {
    UIViewController *resultVC;
    //这里的window有时取得不是delegate的window
    //自己可以自行验证下面两行代码的区别。
    resultVC = [self _topViewController:[[UIApplication sharedApplication].keyWindow rootViewController]];
    resultVC = [[[UIApplication sharedApplication] delegate] window].rootViewController;
    while (resultVC.presentedViewController) {
        resultVC = [self _topViewController:resultVC.presentedViewController];
    }
    return    resultVC;
}
+ (UIViewController *)_topViewController:(UIViewController *)vc {
    if ([vc isKindOfClass:[UINavigationController class]]) {
        return [self _topViewController:[(UINavigationController *)vc topViewController]];
    } else if ([vc isKindOfClass:[UITabBarController class]]) {
        return [self _topViewController:[(UITabBarController *)vc selectedViewController]];
    } else {
        return vc;
    }
    return nil;
}

在stackoverflow上检索到这样一句话: [链接见参考]

When they are different, usually you are presenting another window other than the app delegate's main window. Your app can have many windows but only the keyWindow is the window that is visible on the screen and receiving events (example could be a UIAlert when visible and receiving events it is the keywindow) reference:

大概意思就是:
当你的窗口不止一个UIWindow对象时(这个新的window是可见的),keyWindow就不是[[UIApplication sharedApplication] delegate] window.了。

问题实践

下面的场景验证,都是打印下面三个Window的内存地址。

  UIWindow  *delegateWindow    = [[[UIApplication sharedApplication] delegate] window];
  UIWindow  *keyWindow         = [UIApplication sharedApplication].keyWindow;
  UIWindow  *lastObjectWindow  = [UIApplication sharedApplication].windows.lastObject;
    //添加键盘监听,并打印每个时刻的keyWindow对象
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShowNotification:) name:UIKeyboardDidShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHideNotification:) name:UIKeyboardWillHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
    - (void)keyboardWillShow:(NSNotification*)notification{}
    - (void)keyboardDidShowNotification:(NSNotification*)notification{}
    - (void)keyboardWillHideNotification:(NSNotification*)notification{}
    - (void)keyboardDidHide:(NSNotification*)notification{}
系统版本 描述信息
12.0 无论是在那个方法,当前打印windows都会有三个Window,UIWindowUITextEffectsWindowUIRemoteKeyboardWindowkeyWindow和delegate window内存地址相同
9.1 无论是在那个方法,当前打印windows都会有三个Window,UIWindowUITextEffectsWindow UIRemoteKeyboardWindowkeyWindow和delegate window内存地址相同
8.1 无论是在那个方法,当前打印windows都会有三个Window,UIWindowUITextEffectsWindow UIRemoteKeyboardWindowkeyWindow和delegate window内存地址相同
属性 描述信息
UIAlertView _UIAlertControllerShimPresenterWindow,内存地址已经不同 (iOS 8.1
UIActionSheet _UIAlertControllerShimPresenterWindow,内存地址已经不同 (iOS 8.1)

基本上大多数maskView,为了不让alert视图或者keyBoard视图遮住,基本上都会选择新建Window,此类方法需要特殊条件才能测试。

    //可自行测试伪代码,新建Window
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    window.windowLevel = UIWindowLevelAlert;
    window.rootViewController = viewController;
    [window makeKeyAndVisible];
问题总结
  1. 当视图出现键盘输入操作时,delegate window 和keywindow是不变的
  2. 当出现UIAlertView或者UIActionSheet等提示框类视图时,delegate window 和keywindow已经不一样,这点需要注意。
  3. 工程出现太多topViewController跳转时,这种太违背软件设计原则,反向传值问题非常大。
  4. 当出现侧滑栏这样的设计时,无论是通过delegate window或者keywindow,递归取值可能就会有问题,还是老老实实找到Super ViewController才是硬道理。
参考链接

https://stackoverflow.com/questions/21698482/diffrence-between-uiapplication-sharedapplication-delegate-window-and-u

上一篇下一篇

猜你喜欢

热点阅读