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

声明:我为这个框架写了三篇文章:
第一篇:[iOS]UINavigationController全屏pop之为每个控制器自定义UINavigationBar。这篇文章主要是讲述如何实现自定义导航栏的,所有的思路和实现都是 JNTian的。
第二篇:[iOS]UINavigationController全屏pop之为每个控制器添加底部联动视图。这篇文章讲述,如何在已有的自定义导航栏基础上添加自定义的“底部联动视图”。所有的思路和实现都是我自己的。
第三篇:[iOS]UINavigationController全屏pop之为控制器添加左滑push。这次将讲述如何实现左滑push到绑定的控制器中,并且带有push动画。


 112122663-f6bfe46dfc790852

框架特性

✅ 全屏pop手势支持
✅ 全屏push到绑定的控制器支持
✅ 为每个控制器定制 UINavigationBar 支持(包括设置颜色和透明度等)
✅ 为每个控制器添加底部联动视图支持
✅ 自定义pop手势范围支持(从屏幕最左侧开始计算宽度)
✅ 为单个控制器关闭pop手势支持
✅ 为所有控制器关闭pop手势支持

说明:有很多同学,包括你能看到,留言栏里也有同学说原作者不推荐使用这个框架。然后就来问我,是否能在自己的项目里引入这个框架。从技术的角度来说,我个人的想法是,你要尝试自己去衡量。可能很多同学都看到原作者不推荐,但是却没注意不推荐的原因:“细节问题会比这个完善很多”。我不解释,只把原因单独贴出来了。补充一点,凡是我在自己的项目中使用碰到的细节问题,都已经修复好了。
有一个同学担心性能问题,他的担心是有依据的,因为毕竟经过包装处理以后,以前每次只需要压入一个控制器到栈,现在是每次压入三个。其实碰到这种问题,我是尝试去做循环利用的,但是由于这里问题的特殊性(为什么特殊?),我至今仍在思考合适的方法能够达成循环利用的效果。为了解除这部分同学的疑问,你可以使用以下网易云音乐,他们是这种模式的缔造者,同时通过Reveal观察,没有迹象表明他们实现了这部分的循环利用,这就是我对性能问题的交代。如果有一天我找到了一个合适的方式来做循环利用我肯定还会写一篇文章来说明这个过程的。

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

关于demo,总结一下

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

一、原作 JNTian的思路?

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

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

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

二、究竟怎么实现的?

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

  • setNavigationBarHidden:是直接将navigationBar给移除,而navgationBar.hidden = YES只是让navigationBar变透明了
  • 在苹果的规则里UINavigationControler嵌套UINavigationControler的方式是不被允许的,也不能执行压入栈push和出栈pop动作

跟我看三张图:

132122663-7e5f0f67829f6593

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

  • 窗口的根控制器是TabBarController(上图紫色的view)
  • TabBarController每一个分支子控制器,用一个根导航控制器作为管理者(上图小一点的黑色View)
  • 上面我们说的第一个原则setNavigationBarHidden:和 navgationBar.hidden = YES的区别,这里使用setNavigationBarHidden:将根导航控制器的导航栏彻底移除
142122663-55063018b905c0e3
  • 每当用户设置根导航控制器的rootViewController和push入栈的时候,要先将传进来的控制器(上图蓝色的View)先包装一层导航控制器(上图白色的View)
  • 上面我们说的第二个原则,UINavigationControler嵌套UINavigationControler的方式是不被允许的,也不能执行压入栈push和出栈pop动作
  • 所以我们要在导航控制器(上图白色的View)外层再包装一层UIViewController
  • 经过以上的包装以后,用户传进来的Controller就拥有了一个自己专有的导航控制器,我们可以在对应的控制器的.m文件里对该导航控制器做任何自定义(设置透明度和颜色以及渐变)
  • 而且同时也实现了,最重要的一点,要求全屏右滑返回的时候,导航条跟随自己的控制器滑动
152122663-f76b7e926a7a0684

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

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

三、代码实现?

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

四、更新

  • 2016.08.02:
    有朋友在QQ上联系我说,他在实际开发中有需要在某个界面暂时关闭右滑手势的需求。所以,加入暂时关闭右滑手势开关,方便在某些情况下需要暂时关闭手势。具体更新见我的GitHub_Demo
  • 2016.08.04:
    上个版本使用类工厂方法,在类工厂方法里操作了用户传进来的控制器的View,所以会造成在应用启动的时候TabBarVC的子控制器一起加载,有性能问题.这次提交修复了这个问题, TabBarVC的子控制器都能遵循懒加载的原则.谢谢:袁小荣同学(Github)的提醒.
  • 2016.08.08:
    这个框架有了较大的更新。具体包括给现有的Pop手势添加暂时关闭开关,以及自定义响应手势范围。在现有基础上,框架添加了底部联动视图,具体实现以及思路,请前往我的文章:1行代码为每个Controller自定义“TabBar”
  • 2016.09.13
    添加了直接拿到根导航控制器的接口,方便使用popToViewController功能。具体见demo第三个控制器的Pop。

我的文章集合

下面这个链接是我所有文章的一个集合目录。这些文章凡是涉及实现的,每篇文章中都有GIT地址,GIT上都有源码。如果某篇文章刚好在你的实际开发中帮到你,又或者提供一种不同的实现思路,让你觉得有用,那就看看这句话 “坚持每天点赞的人,99%都是帅哥美女,再也不用单身了😀”

 

如果你有问题,除了在文章最后留言,还可以在微博@盼盼_HKbuy上给我留言,以及访问我的Github
1 收藏 评论

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部