iOS学习笔记-从QQ的一个Bug引发的关于导航栏的思考 | luckymore的学习笔记
一次偶然的机会,发现了QQ的一个Bug。
复现过程如下:
- 在QQ中通过任意途径打开一个pdf或者word文档
- 单击全屏显示
- 滑动返回到一半终止返回(再次回到浏览界面)
- 再次滑动返回,就会观察到这样的场景————导航栏消失不见或者导航栏错位
当然,这是一个必现的Bug,而且我也相信类似的操作在很多别的应用中也会引发这个Bug,归根结底,这是iOS在引入了滑动返回后导航栏本身的一个Bug
问题复现
为了验证这个问题,我特地用storyboard拖了3个viewController,其实前两个的导航栏设置为不隐藏,第三个导航栏设置为隐藏,并且在viewWillAppear和viewWillDisappear中对导航栏做了处理。
最后的实验结果出乎意料!!
iOS居然直接屏蔽了从无导航栏滑动返回到有导航栏的操作。
我可以从第二个界面滑动返回到第一个界面,但是没有办法从第三个界面滑动返回到第二个界面?
为此!我特地自定义了滑动返回事件,来复现这个问题。
源代码见:https://github.com/luckymore0520/NavigationTest
下面我们来构想这样一个场景,应用中大部分的界面是具有导航栏的,只有少数几个是没有导航栏的。那么我们这么写:
如果有基类viewController,会在viewWillAppear中添加setNavigationBarHidden = NO的方法,并且在需要隐藏导航栏的viewController的viewWillAppear中setNavigationBarHidden:YES 同时在viewWillAppear的时候设为NO;
我在storyboard中拖了4个viewController,分别设为1、2、3、4,其中3是没有导航栏的,界面切换的顺序是:
1-2-3
2-4
简单的说,1为root,2可以到3和4
当我们从没有导航栏的3滑动返回到2,并且第一次取消滑动返回,第二次再次滑动返回的时候,表面上界面2没有任何问题,但是此时,无论你进入4还是返回1,导航栏上始终写着2!也就是说,本该属于界面2的导航栏阴魂不散地留在了导航栏上面,返回按钮也变得无效,必须使用滑动返回
这个问题是必现的,也是比较离奇的=.=


讨论
下面我们来讨论一下为什么会产生这样的问题:
我们往往会在viewWillAppear和viewWillDisappear中通过setNavigationBarHidden的方法来处理导航栏的显示和隐藏,在正常情况下,这不会引起任何问题,因为单纯的单击返回,这些方法的生命周期是这样的
- Current viewWillDisappear
- New viewWillAppear
- Current viewDidDisappear
- New viewDidAppear
而如果是取消了的滑动返回,它的生命周期是这样的
- Current viewWillDisappear
- New viewWillAppear
- New viewWillDisappear
- New viewDidDisappear
- Current viewWillAppear
- Current viewDidAppear
假设从A push 到B
在A的viewWillAppear中设置navigationBarHidden = NO;
在B的viewWillAppear中设置navigationBarHidden = YES;
viewWillDisappear中设置navigationBarHidden = NO;
把相关数据打印下来得出的结果是这样的:
取消滑动返回
- willDisappear 3 currentViewController 2
- willAppear 2 currentViewController 2
- willDisappear 2 currentViewController 3
- viewDidDisappear 2 currentViewController 3
- willAppear 3 currentViewController 3
- viewDidAppear 3 currentViewController 3
再次滑动返回不取消
- willDisappear 3 currentViewController 2
- willAppear 2 currentViewController 2
- viewDidDisappear 3 currentViewController (null)
- viewDidAppear 2 currentViewController 2
如果直接滑动不取消,出来的数据同上
所以问题必然处在取消滑动返回的顺序上
个人猜测问题就出在第三句,在currentViewController=3的情况下调用了2的viewWillDisappear。造成了导航栏的错乱,以至于引发了之后的一系列问题。
再深入研究下去,我也不知道究竟是什么触发了导航栏的错乱了0.0
解决方案
对于这个问题的解决方案,事实上可以用一个很trick的方法来预防导航栏的错乱,那就是用设置导航栏的alpha值来替代hidden,实践证明,当导航栏设为alpha=0后可以达成setHidden=YES一样的效果。
当然,苹果官方并不建议对导航栏进行setHidden和setTranslucent以外的操作,事实证明,设置alpha会引发一些其他的问题。
至于哪些问题…
我决定下次说啦~