UINavigationController 全屏 pop 之为每个控制器自定义 UINavigationBar

2018-01-13 11:00:04来源:https://juejin.im/post/5a3118f46fb9a0451238f2c6作者:稀土掘金人点击

分享

:white_check_mark: 全屏 pop 手势支持


:white_check_mark: 全屏 push 到绑定的控制器支持


:white_check_mark: 为每个控制器定制 UINavigationBar 支持(包括设置颜色和透明度等)


:white_check_mark: 为每个控制器添加底部联动视图支持


:white_check_mark: 自定义 pop 手势范围支持(从屏幕最左侧开始计算宽度)


:white_check_mark: 为单个控制器关闭 pop 手势支持


:white_check_mark: 为所有控制器关闭 pop 手势支持


:heart: 当当前控制器使用 AVPlayer 播放视频的时候, 使用自定义的 pop 动画以保证 AVPlayer 流畅播放.





说明:有很多同学,包括你能看到,留言栏里也有同学说原作者不推荐使用这个框架。然后就来问我,是否能在自己的项目里引入这个框架。从技术的角度来说,我个人的想法是,你要尝试自己去衡量。可能很多同学都看到原作者不推荐,但是却没注意不推荐的原因:“细节问题会比这个完善很多”。我不解释,只把原因单独贴出来了。补充一点,凡是我在自己的项目中使用碰到的细节问题,都已经修复好了。


有一个同学担心性能问题,他的担心是有依据的,因为毕竟经过包装处理以后,以前每次只需要压入一个控制器到栈,现在是每次压入三个。其实碰到这种问题,我是尝试去做循环利用的,但是由于这里问题的特殊性(为什么特殊?),我至今仍在思考合适的方法能够达成循环利用的效果。为了解除这部分同学的疑问,你可以使用以下网易云音乐,他们是这种模式的缔造者,同时通过Reveal观察,没有迹象表明他们实现了这部分的循环利用,这就是我对性能问题的交代。如果有一天我找到了一个合适的方式来做循环利用我肯定还会写一篇文章来说明这个过程的。


As we all know,Apple 提供的导航条是用来管理窗口控制器的结构的,某个 UINavigationController 执行 Push 操作后,该导航控制器管理的 controllers 共用一个导航条,如果对该导航条进行自定义,那么各个界面的导航条都会变成自定义以后的样式。但是,实际开发中,我们可能需要为不同的控制器定制不同的导航条,就像我 demo 里写的一样.


关于demo,总结一下
这个 demo 的结构是这样的, UITabBarController 作为根控制器管理着一个导航控制器和一个 UIViewController
导航控制器下面有一个 UITableViewController ,为了完整的显示图片,该 UITableViewController 的导航栏要求是透明的
push 到下一个控制器的时候,要求导航栏正常显示
再 push 下一个界面,要求导航栏的颜色是红色
最后,最重要的一点,要求全屏右滑返回的时候,导航条跟随自己的控制器滑动
01、原作 JNTian的思路?

用 Reveal 观察了几个常用 App,发现了这种效果的实现大致分3种:


第一种是使用自定义 navigationBar .淘宝,网易新闻,达令等使用的是这种. 第二种是用截图的办法,在 push 到下一个页面时,截取屏幕,在使用 edgePan 来 pop 时看到的就是背后的截图,也能实现这种效果.京东,天猫等使用的是这种. 第三种是使用了一种比较特别,比较巧妙的办法实现的,也就是网易云音乐的实现方法,后面会分析一下这种实现.


作者用 Reveal 工具考察了实现这一效果的三种已知的方式,在对比这三个实现方式各自的优缺点以后,采用了第三种方式来实现。


02、究竟怎么实现的?

要探究实现,先要讲清楚一个原则:


在苹果的规则里 UINavigationControler 嵌套 UINavigationControler 的方式是不被允许的,也不能执行压入栈 push 和出栈 pop 动作

跟我看三张图:





还记得开头的 demo 吗?demo 的结构就是这样一个窗口结构图:


窗口的根控制器是 TabBarController (上图紫色的 view )
TabBarController 每一个分支子控制器,用一个根导航控制器作为管理者(上图小一点的黑色 View )
使用 setNavigationBarHidden: 将根导航控制器的导航栏彻底移除



每当用户设置根导航控制器的 rootViewController 和 push 入栈的时候,要先将传进来的控制器(上图蓝色的 View )先包装一层导航控制器(上图白色的 View )
上面我们说的第二个原则, UINavigationControler 嵌套 UINavigationControler 的方式是不被允许的,也不能执行压入栈 push 和出栈 pop 动作
所以我们要在导航控制器(上图白色的 View )外层再包装一层 UIViewController
经过以上的包装以后,用户传进来的 Controller 就拥有了一个自己专有的导航控制器,我们可以在对应的控制器的 .m 文件里对该导航控制器做任何自定义(设置透明度和颜色以及渐变)
而且同时也实现了,最重要的一点,要求全屏右滑返回的时候,导航条跟随自己的控制器滑动



上面两张图,我们分析了怎么设定根视图的结构,以及怎么包装用户传进来的控制器。 有了以上的基础以后,我们就可以进行入栈和出栈的操作了。 注意:


TabBarController 每一个分支子控制器的 push 和 pop 操作都是由根导航控制器负责
pop 和 push 操作的对象都是我们包装过后的 WarpViewController ,这样就不会有导航控制器 push 和 pop 导航控制器的冲突了
而且,毫无疑问,也必须设定一个开关来支持全屏右滑返回。
03、代码实现?

代码实现见 GitHub 。或者也可以参见原作者的 Github地址 。我只是在原作的基础上加了注释,并没有修改代码。 如果你对demo中图片在tableView滚动时的视差效果感兴趣,可以参见我以前的文章 仿Airbnb的tableView头部视图层叠效果 。 最后,谢谢Github开源作者: JNTian,大写的感谢。


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台