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

作者 : 开心源码 本文共4181个字,预计阅读时间需要11分钟 发布时间: 2022-05-12 共210人阅读
问题关键

[[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;
  • UITextView | UITextField
    //增加键盘监听,并打印每个时刻的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 | UIAlertController | UIActionSheet
属性形容信息
UIAlertView_UIAlertControllerShimPresenterWindow,内存地址已经不同 (iOS 8.1
UIActionSheet_UIAlertControllerShimPresenterWindow,内存地址已经不同 (iOS 8.1)
  • maskView

基本上大多数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

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 记录一次iOS无法获取topViewController的问题

发表回复