美文网首页iOS开发iOS开发者进阶
《iOS开发之路》使用keyWindow的坑 多window必看

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

作者: TheLazyCoder | 来源:发表于2018-11-23 16:33 被阅读107次

    踩坑过程:

    公司项目中使用科大讯飞语音识别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];
    

    相关文章

      网友评论

        本文标题:《iOS开发之路》使用keyWindow的坑 多window必看

        本文链接:https://www.haomeiwen.com/subject/xwalqqtx.html