美文网首页iOS Developer
聊聊一个关于UIAlertController的“熊猫”Bug

聊聊一个关于UIAlertController的“熊猫”Bug

作者: 皮皮Warrior | 来源:发表于2017-01-08 19:33 被阅读415次

“熊猫”Bug是指一种极难出现、难以摸索原因的bug。


今天产品在众测平台上发现了一个“熊猫”bug,这个bug的表现很奇怪,弹出的UIAlertController的按钮没有文字,但title和message有文字。这个bug我们无法重现,在外网只有两个用户的手机上出现,出现之后就是必现的。

alertActionSheet alertView

这个bug刚拿到手有点茫然,不明白为什么使用系统的弹窗会有这种问题,一度怀疑是苹果的Bug。

建立猜想

由于这个bug只有外网的两个用户能重现,所以初步的方案是先补一些log, 增加一些实验的代码,再打个包让用户去试,看看能否查得原因。
由这个bug的变现,初步猜想这个bug可能成因:

  1. 没有在主线程弹出alertController(看用户的log排除了)
  2. 按钮的字是白色的(这个自己实验就排除了)
  3. 弹出时有其他动画冲突,导致没有渲染,需要手动调用setNeedsDisplay
  4. 按钮layout时出了问题
  5. 系统的bug(bug只在Qzone出现)
  6. 需要手动设一下tintColor

根据以上的猜想,总结出了3个解决方案,打包给用户实验:

  1. 弹出前手动设一下tintColor
  2. 弹出前手动调用setNeedsDisplay
  3. 什么也不做,排除环境影响

同时在弹出时,把alertController的UI层级打印出来,判断是不是layout除了问题。

补log,打包

增加三个按钮,让用户点击后呼出弹窗,来判断上面的3种解决方案的效果:

//点击按钮时
- (IBAction)tapBugfixButton:(id)sender{
    self.bugfixType = [(UIButton*)sender tag];
}
//呼出alertController
- (void)showAlert{
    //alert测试包
    if (self.bugfixType == 1001) {
    //之前看log alertController.view.tintColor是有值的,所以再设置一遍试试
        alertController.view.tintColor = alertController.view.tintColor;
        QZLVLOG_INFO(@"************bugfix56118199 alertView set tintColor ************");
    }else if (self.bugfixType == 1002){
        [alertController.view setNeedsDisplay];
        QZLVLOG_INFO(@"************bugfix56118199 alertView setNeedsDisplay ************");
    }else if (self.bugfixType == 1003){
        QZLVLOG_INFO(@"************bugfix56118199 alertView do nothing ************");
    }
    self.alertCtrl = alertController;
    [self performSelector:@selector(needDescribeUI) withObject:nil afterDelay:0];
}
//打印UI层级
- (void)needDescribeUI{
    NSString *description = [self.alertCtrl.view performSelector:@selector(recursiveDescription)];
    QZLVLOG_INFO(@"**********bugfix56118199 describe UI architecture**********\n%@\n",description);
}

打包给用户后,用户反馈,只有第一种方案有效,但为什么setTintColor方法才会渲染按钮?alertController.view.tintColor本身不就有值吗?
再查看用户的alertView的UI层级log,对比正常的alertView,发现一处异常:

//出bug的alertView层级
<_UIAlertControllerView: 0x11357c270; frame = (10 378.5; 300 179.5); tintColor = UIExtendedSRGBColorSpace 0 0.25098 0.866667 1; layer = <CALayer: 0x174a3dbc0>>
……
                                    <_UIAlertControllerActionView: 0x11422e010; frame = (0 0; 300 57); Action = <UIAlertAction: 0x17030c600 Title = "�照" Descriptive = "(null)" Image = 0x0>>
|    |    |    |    |    |    |    |    | <UIView: 0x114237800; frame = (129.5 0; 41 57); userInteractionEnabled = NO; layer = <CALayer: 0x170c34500>>
|    |    |    |    |    |    |    |    |    | <UILabel: 0x11422c5d0; frame = (0 16.5; 41 24); text = '�照'; userInteractionEnabled = NO; tintColor = UIExtendedSRGBColorSpace 0 0.200784 0.693333 1; layer = <_UILabelLayer: 0x170484e20>>

//正常的alertView层级
<_UIAlertControllerView: 0x11357f2e0; frame = (10 378.5; 300 179.5); layer = <CALayer: 0x174c2da80>>
                                        <_UIAlertControllerActionView: 0x11357d8e0; frame = (0 0; 300 57); Action = <UIAlertAction: 0x174315600 Title = "�照" Descriptive = "(null)" Image = 0x0>>
   |    |    |    |    |    |    |    |    | <UIView: 0x11358b2c0; frame = (129.5 0; 41 57); userInteractionEnabled = NO; layer = <CALayer: 0x174a21880>>
   |    |    |    |    |    |    |    |    |    | <UILabel: 0x11358b460; frame = (0 16.5; 41 24); text = '�照'; userInteractionEnabled = NO; tintColor = UIExtendedSRGBColorSpace 0 0 0 0; layer = <_UILabelLayer: 0x17448cfd0>>

可以看到tintColor = UIExtendedSRGBColorSpace 0 0 0 0,alertView的按钮Label的tintColor是RGBA(0,0,0,0),alpha值是0,所以才没有显示。原来alertController.view.tintColoralertController.alertControllerActionView.label.tintColor不是一个值!这就解释了alertController.view.tintColor有值但按钮不显示的现象。

确认猜想,查原因

现在确认了是tintColor的原因,接下来就开始查alertControllerActionView.label.tintColor在哪被改变了,可是alertControllerActionView是系统控件的私有接口,无法获取,更别说改变了。
一筹莫展的时候,看到了这个bug报告,其中说道:

When an application window's tintColor is set to white, any UIAlertController launched bears the same tintColor even when the instance of the UIAlertContoller is set to something else

由这个线索追查到,有问题的alertView都是在一个新建的UIWindow被弹出,弹出代码如下:

//新建alertController
//……

//新建一个Window
- (UIWindow *)alertWindow
{
    if (!_alertWindow) {
        _alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        _alertWindow.rootViewController = [QZLVForbidAutoRotateController new];
        _alertWindow.windowLevel = NSUIntegerMax;
        _alertWindow.hidden = NO;
        _alertWindow.userInteractionEnabled = YES;
        _alertWindow.tintColor = [[UIWindow valueForKey:@"keyWindow"] tintColor];
    }
    
    return _alertWindow;
}

//弹出alertController
[[self alertWindow].rootViewController presentViewController:alertController animated:YES completion:nil];

在本地将_alertWindow.tintColor设为RGBA(0,0,0,0)后,复现了这个bug。同时再一次补log打包给用户,确认了这个原因。

alertActionSheetalertActionSheet alertViewalertView

原来是在代码的其他地方,修改了keyWindow的tintColor为RGBA(0,0,0,0),导致了_alertWindow.tintColor被赋值为了透明色。而苹果的默认实现中,将alertController的按钮Label(alertControllerActionView.label.tintColor)和弹出它的window.tintColor相等,导致了这个bug。

总结

看来苹果关于UIAlertController的实现是有坑的,除非手动设置alertController.view.tintColor,否则alertControllerActionView.label.tintColor与presenter的window.tintColor是相关联的,让人有点摸不到头脑。不过测试了同样的代码,使用UIAlertView就不会有这个bug。
以后使用UIAlertController时,这一点要多多注意了。
这次这个bug时间比较紧急,leader、导师、同事们给了我很多建议和指导,看来查bug方法论还是很重要的。

相关文章

网友评论

    本文标题:聊聊一个关于UIAlertController的“熊猫”Bug

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