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

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

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

后来在Debug View Hierarchy(视图层级调试)中查看分解图,这个时候需要两个图片镇一下局面了
《iOS开发之路》使用keyWindow的坑 多window必看_第1张图片
问题到这里就很明确了,我的IFlyRecognizerView给我添加到了我的最底层window上边,但是这个视图添加过程都是在讯飞静态库中完成,在附一张IFlyRecognizerView.h文件的截图
《iOS开发之路》使用keyWindow的坑 多window必看_第2张图片
根据这个文件和视图层级图来分析,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如下:

 第一次:; layer = >
 第二次:; layer = >
 第三次:; layer = >
 第四次:<_UIAlertControllerShimPresenterWindow: 0x7fc1a8d0f2c0; frame = (0 0; 375 667); opaque = NO; gestureRecognizers = ; layer = >

可以很清晰的看到,我在第三次打印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];

你可能感兴趣的:(iOS开发之路)