iOS学习笔记-从QQ的一个Bug引发的关于导航栏的思考

Bug的发现

一次偶然的机会,发现了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
storyboard

当我们从没有导航栏的3滑动返回到2,并且第一次取消滑动返回,第二次再次滑动返回的时候,表面上界面2没有任何问题,但是此时,无论你进入4还是返回1,导航栏上始终写着2!也就是说,本该属于界面2的导航栏阴魂不散地留在了导航栏上面,返回按钮也变得无效,必须使用滑动返回
这个问题是必现的,也是比较离奇的=.=
第四页
第一页
此处输入图片的描述

讨论

下面我们来讨论一下为什么会产生这样的问题:

我们往往会在viewWillAppear和viewWillDisappear中通过setNavigationBarHidden的方法来处理导航栏的显示和隐藏,在正常情况下,这不会引起任何问题,因为单纯的单击返回,这些方法的生命周期是这样的

  1. Current viewWillDisappear
  2. New viewWillAppear
  3. Current viewDidDisappear
  4. New viewDidAppear

而如果是取消了的滑动返回,它的生命周期是这样的

  1. Current viewWillDisappear
  2. New viewWillAppear
  3. New viewWillDisappear
  4. New viewDidDisappear
  5. Current viewWillAppear
  6. Current viewDidAppear

假设从A push 到B
在A的viewWillAppear中设置navigationBarHidden = NO;
在B的viewWillAppear中设置navigationBarHidden = YES;
viewWillDisappear中设置navigationBarHidden = NO;

把相关数据打印下来得出的结果是这样的:
取消滑动返回

  1. willDisappear 3 currentViewController 2
  2. willAppear 2 currentViewController 2
  3. willDisappear 2 currentViewController 3
  4. viewDidDisappear 2 currentViewController 3
  5. willAppear 3 currentViewController 3
  6. viewDidAppear 3 currentViewController 3

再次滑动返回不取消

  1. willDisappear 3 currentViewController 2
  2. willAppear 2 currentViewController 2
  3. viewDidDisappear 3 currentViewController (null)
  4. viewDidAppear 2 currentViewController 2

如果直接滑动不取消,出来的数据同上
所以问题必然处在取消滑动返回的顺序上
个人猜测问题就出在第三句,在currentViewController=3的情况下调用了2的viewWillDisappear。造成了导航栏的错乱,以至于引发了之后的一系列问题。
再深入研究下去,我也不知道究竟是什么触发了导航栏的错乱了0.0

解决方案

对于这个问题的解决方案,事实上可以用一个很trick的方法来预防导航栏的错乱,那就是用设置导航栏的alpha值来替代hidden,实践证明,当导航栏设为alpha=0后可以达成setHidden=YES一样的效果。

当然,苹果官方并不建议对导航栏进行setHidden和setTranslucent以外的操作,事实证明,设置alpha会引发一些其他的问题。

至于哪些问题…
我决定下次说啦~