《iOS开发之路》使用keyWindow的坑 多window必看

####踩坑过程:
公司项目中使用科大讯飞语音识别IFlyRecognizerView类时,出现了安装后首次启动后无法正常弹出语音识别框。然后就开始了刨根问底之旅。

1、IFlyRecognizerView的问题,后来查看二次启动时,语音识别框弹出正常。
2、后来认为是语音识别第一次启动需要配置参数无法使用,后来查看讯飞demo首次安装后,语音识别框弹出正常。
3、随后查看了相关类的初始化状态,发现都初始化成功。
4、在IFlyRecognizerView初始化过程中会弹出一个在异步线程调用UI进程的警告,但是第二次启动同样不会影响语音识别框弹出。

后来在Debug View Hierarchy(视图层级调试)中查看分解图,这个时候需要两个图片镇一下局面了😏
项目界面太漂亮,只放一下层级图吧
问题到这里就很明确了,我的IFlyRecognizerView给我添加到了我的最底层window上边,但是这个视图添加过程都是在讯飞静态库中完成,在附一张IFlyRecognizerView.h文件的截图
IFlyRecognizerView.h
根据这个文件和视图层级图来分析,IFlyRecognizerView这个类弹出语音识别框,最有可能就是直接操作了keyWindow,然后把语音识别框添加到了keyWindow上。

这个时候就需要查看一下xcode文档了。

The app's key window.
This property holds the [UIWindow] object in the [windows] array that is most recently sent the [makeKeyAndVisible] message.

翻译之后的意思:

应用程序的关键窗口。
这个属性保存了[windows]数组中的[UIWindow]对象,该对象最近被发送了[makeKeyAndVisible]消息。

简单的说那个window调用了makeKeyAndVisible方法,那个window就是keyWindow。这个时候原因就很清楚了,项目中主window之后,在使用别的window时调用了makeKeyAndVisible,但是销毁别的window后,默认的keyWindow还是被销毁的那个。好吧有一点绕😏,这个时候需要上一下代码了。

    self.window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    self.window.rootViewController = [[UIViewController alloc] init];
    [self.window makeKeyAndVisible];
    NSLog(@"第一次:%@",[UIApplication sharedApplication].keyWindow);

    self.window2 = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    self.window2.rootViewController = [[UIViewController alloc] init];
    [self.window2 makeKeyAndVisible];
    NSLog(@"第二次:%@",[UIApplication sharedApplication].keyWindow);

    [self.window2 removeFromSuperview];
    self.window2 = nil;
    NSLog(@"第三次:%@",[UIApplication sharedApplication].keyWindow);
    
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:@"测试" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确认", nil];
    [alertView show];
    NSLog(@"第四次:%@",[UIApplication sharedApplication].keyWindow);

运行后打印log如下:

 第一次:<UIWindow: 0x7fc1a8c05350; frame = (0 0; 100 100); gestureRecognizers = <NSArray: 0x6080000558a0>; layer = <UIWindowLayer: 0x60800003ad60>>
 第二次:<UIWindow: 0x7fc1ada09810; frame = (0 0; 200 200); gestureRecognizers = <NSArray: 0x60400005cdd0>; layer = <UIWindowLayer: 0x60400003bbc0>>
 第三次:<UIWindow: 0x7fc1ada09810; frame = (0 0; 200 200); gestureRecognizers = <NSArray: 0x60400005cdd0>; layer = <UIWindowLayer: 0x60400003bbc0>>
 第四次:<_UIAlertControllerShimPresenterWindow: 0x7fc1a8d0f2c0; frame = (0 0; 375 667); opaque = NO; gestureRecognizers = <NSArray: 0x6000000517f0>; layer = <UIWindowLayer: 0x6000000390a0>>

可以很清晰的看到,我在第三次打印log之前,self.window2已经被销毁,手机页面这时显示的是self.window,但是keyWindow指向的还是self.window2。

######PS:第四次的打印,只是为了证明UIAlertView也是系统基于window来弹窗,所以在弹窗显示时,你使用keyWindow添加的customView都会添加到UIAlertView所在的window上。会导致customView随UIAlertView同时消失。

#解决方法#
1、使用完别的window时,及时使用当前window调用一下makeKeyAndVisible方法。
2、在UIView的属性中有一个属性:

@property(nullable, nonatomic,readonly) UIWindow     *window;

可以通过当前显示的任意一个view的window,调用makeKeyAndVisible方法,来更新最新的keyWindow。

[self.view.window makeKeyAndVisible];
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值