-

一、被切断的小红点

(一)问题描述

3年前我负责导航栏设计的时候,在红点处理这块遇到一个很神奇的问题。

简单描述是这样:

A push B页面,在B页面导航栏右边部分渲染了一个红点,但在左滑退出时,B导航栏上的红点被裁断了。

刚开始我觉得很奇怪,业内也没有人反馈过这个问题,我还特意去看了美团app,发现他们也有这样的问题:

当然了,别人也有这样的问题,这并不是一个好消息,说明这反而不是一个简单的问题。

别人没处理,但我们需要处理。

(二)问题处理

debug的过程我在这里就不多说了,我直接公布原因和处理方案。

  1. 为什么导航栏会被裁断?

iOS11所有的UIBarButtonItem都是加载到新的_UIButtonBarStackView上的,而_UIButtonBarStackView默认在5.5英寸机型有20px,其余机型为16px的边距。

在pop转场时,实际是系统对导航栏做了一个快照snapshot,这个快照把左右两边都截去了20px。

  1. 怎么处理?

在介绍解决方案之前,我们先明确一下 UINavigationBar 的渲染结构, UINavigationBar 的渲染结果本质是MVC作用的,其大致结构可以抽象如下:

  • NavigationBar
    • ContentView
    • ContentItem

其中contentView担当的是View的角色,ContentItem担当的是Model的角色,Controller由NavigationController来承担,这里我们重点看contentView和ContentItem。

所有我们操作的非直接加到 NavigationBar 上的元素,都是通过设置 ContentItem ,然后由 ContentItem 驱动渲染到 ContentView 上的。

比如:self.title / self.navigationItem.titleView 。

好的,接下来我们要介绍的重点是:pop时生成的快照snapshot,其实是对 ContentView 的截图,如果我们把一个视图AAA主动的加到NavigationBar上,在pop时生成的快照是不会把AAA包含进去的。

所以解决方案就很清楚了:

我们把红点主动加到 NavigationBar 上,在willDisappear移出,在willAppear时添加上即可。

二、错乱的导航栏

(一)问题描述

这也是一个很麻烦的问题,我们都知道,每个页面有自己页面设置的导航栏,每个导航栏自己设置NavigationItem更新UI。

但这里遇到的一个问题是:导航栏的MVC机制失效了。

具体表现是:

A push B页面之后,在 B页面执行pop返回A页面之后,A页面的导航栏展示的还是B页面设置的。

(二)问题处理

我们都知道系统会在pop/push转场后根据NavigationItem去更新NavigationBar的视图,

我最初尝试过主动重新设置页面A的navigationItem,结果发现并没有效果。

那这就说明一个情况:在该情况下,A页面导航栏的MVC已经失效了,model和view并不是配对的了。

苹果写出bug,就需要我们来修bug了,我们要手动把导航视图进行修正,那怎么处理?

  1. 递归遍历NavigationBar里异常的View,全部remove掉

  2. 主动构建一个A页面需要的导航栏视图,贴到NavigationBar上