iOS [UIApplication sharedApplica
1. 概述
本人最近在写一个自定义弹框(去看看), 在不停的跑demo过程中, 发现怎么也无法将弹窗显示出来, 然后打断点进行调试, 发现[UIApplication sharedApplication].keyWindow竟然为nil:
image.png然后各种找原因, 大概原因就是在用[UIApplication sharedApplication].keyWindow获取keywindow的时候, keywindow并没有被创建, 需要在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
中设置window并makeKeyAndVisible
; 我按照这个来做了, 还是不行!!!!
于是继续找, 有的说是iOS7.0会有[UIApplication sharedApplication].keyWindow
获取不到keywindow的情况, iOS8.0就好多了, 这种情况下可以用[[[UIApplication sharedApplication] delegate] window];
代替, 然后试着这样做了, 结果还是现实不出来, 点击调试视图看到我要的弹窗竟然被window盖住了, 有图有真相:
2. 原因分析
前面看了网上一些分析, 其实有时候并不是十分适合自己的情况, 但也能提供很多思路的指导, 本人自己的分析原因大概是这样的:
最主要的原因是本人将创建和弹出弹窗的代码放在了viewWillAppear
方法里面, 为什么这样是不行的呢, 咱们具体代码具体分析, 首先先看AppDelegate.m
里面的代码是这样的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
RSViewController *VC = [[RSViewController alloc] init];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = VC;
[self.window makeKeyAndVisible];
return YES;
}
这里已经设置了makeKeyAndVisible
, 所以说上面说的对应的问题应该是不存在了;
接下来看看创建弹窗的代码, 是在RSViewController.m文件里的:
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
/*
登录的弹框
d*/
RSAlertView *alerV = [[RSAlertView alloc] initIfLogin];
alerV.registerBlock = ^{
NSLog(@"注册的block");
};
alerV.loginBlock = ^(){
NSLog(@"登录的block");
};
[alerV showRSAlertView];
}
看到这里还是看不出问题是吧, 那我们可以试着验证一下是不是"通过[UIApplication sharedApplication].keyWindow
获取keywindow的时候没有创建keywindow并makeKeyAndVisible", 为了便于标注, 我就上截图吧, 在箭头的地方分别加上几个BSLog
还有这里
image.png然后将项目跑起来, 打印出来的结果是这样的:
image.png注意看顺序!!!注意看顺序!!!注意看顺序!!!注意看顺序!!!注意看顺序!!!注意看顺序!!!注意看顺序!!!注意看顺序!!!注意看顺序!!!
果然是刚刚说的原因, 也就是系统在调用 [UIApplication sharedApplication].keyWindow
之后才makeKeyAndVisible
!!!!!!
知道原因在哪了吧!!!!!!
所以为了能继续顺利测试, 我们必须要在makeKeyAndVisible
之后才使用[UIApplication sharedApplication].keyWindow
, 要正确的判断之前还是之后, 就涉及到老生常谈的生命周期了, 这个这里就不赘述了, 要不然又是讲半天, 这里就说怎么弄吧:
很简单, 创建弹框的方法不要放在viewWillAppear也不要放在viewWillAppear
或者- (void)viewDidLayoutSubviews
方法里, 可以放在
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
这样你就可以方便的进行测试了.
注意 :到了这里, 其实使用[UIApplication sharedApplication].keyWindow
或者[[[UIApplication sharedApplication] delegate] window]
都是可以实现效果的; 但还是比较推荐后者, 因为使用前者的话, 当你的app需要跳转到别的app然后返回本app的时候, 有可能会导致UI错乱, 使用后者就不会.
3. 疑惑
虽然上面说了不放在生命周期的方法里面, 但小伙伴们肯定会马上机智的想到一个问题"那我使用的时候难道keywindow就能保证不是nil吗?", 这问题问得好.
但其实大家不必担心, 因为一般项目里面, 弹窗的出现都是需要触发的, 例如服务器返回错误, 或者点击某个按钮的时候才会触发, 这时候是不会出现上面的问题的, 所以小伙伴们可以放心使用.
另外, 我们也会想[UIApplication sharedApplication].keyWindow
获取到的keywindow跟[[[UIApplication sharedApplication] delegate] window]
获取到的有什么不同吗?
这里大家可以看看另外的作者的分析
stackoverflow上的