UINavigationController 全屏 pop(1)之为控制器添加左滑 push

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


112122663-f6bfe46dfc790852

框架特性

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


这是“UINavigationController全屏pop”系列的第三篇文章,这次将讲述如何实现左滑push到绑定的控制器中,并且带有push动画。如果你没有看过我之前的两篇文章,建议你从第一篇开始看。或者你也可以直接去我的Github上查看 JPNavigationController 的源码。


01、引子

用过新闻软件的朋友应该都知道,比方说网易新闻,你如果在它的新闻详情页左滑,它会出现一个push动画打开评论页面。这次我们就来讨论,在基于之前的封装基础上如何实现这个功能。

02、世面上现有的APP都是怎么实现的?

左滑push到下一个页面的功能,借助于Reveal观察,大致可以分为两类:

  • 第一类,以网易新闻、腾讯新闻、凤凰新闻等主流新闻为代表的APP,他们都在新闻详情页绑定了左滑手势push到评论页面的功能。
  • 第二类,以Instagram、Snapchat为代表的国外社交APP,他们在UITabBarController的某些分支上集成了左滑和右滑手势绑定切换到不同的控制器的功能。

通过Reveal观察发现,第一类左滑手势的功能是集成到了当前控制器对应的UINavigationController上。而第二类是采用让window的根控制器上集成一个UICollectionView,然后把每个控制器的view添加到UICollectionViewCell上,这样就可以实现左滑以及右滑切换到不同的控制器的效果。第二类和我常见的新闻页面的子栏目切换是一个道理,相信大家都会实现的。我们现在要讲的就是怎么将左滑手势的功能是集成到了当前控制器对应的UINavigationController上

132122663-4c4024b948ddb6e5

iOS现在主流的框架结构是像上图这样的,如果要像第二类APP那样实现左滑功能,势必需要重新架构项目,这对于很多成熟的APP来说,工作量还是比较繁重的。所以值得尝试的方案是,在不改变现有项目架构的前提下实现左滑push功能。也就是说,要把左滑手势绑定到对应的导航控制器上。

03、AOP面向切面编程思想

iOS工程师都知道runtime,也就是运行时,得益于Objective-C的runtime的特性,我们可以动态的为类添加方法,以及替换系统的实现等。如果把这种行为抽象成为一个更高级的思想的话,就是所谓的AOP(AOP是Aspect Oriented Program的首字母缩写),也就是面向切面编程。关于AOP具体可以看 维基百科 上的解释,或者 知乎 上的回答。这个框架也是基于AOP思想的,所以能够在不侵入用户的项目的条件下实现以上的特性。

04、大致思路

  • 01.首先我们要拿到用户左滑left-slip这个事件
  • 02.接下来要询问用户是否需要给左滑手势绑定对应的push事件
  • 03.如果用户绑定了事件,此时应该创建要push到的控制器
  • 04.跟踪用户的手势,驱动过渡动画
  • 05.完成push手势

05、具体实现

5.1. 首先我们要拿到用户左滑left-slip这个事件

之前我的第二篇文章说过,框架里使用UIPanGestureRecognizer代替了系统的手势,所以我们能够在UIPanGestureRecognizer的代理方法中拿到用户是否左滑了。

5.2. 接下来要询问用户是否需要给左滑手势绑定对应的push事件

首先我们应该创建一个协议,只要遵守协议,并实现协议方法,每个控制器就都能拥有push功能。

因为我们希望在每个页面都能拥有绑定左滑push的功能,所以我们可以把询问用户是否需要push的代理绑定到每个控制器的navigationController上。

5.3. 如果用户绑定了事件,此时应该创建要push到的控制器

由于之前已经为每个控制器添加了检查是否需要push动画的入口。所以,当检测到用户push的时候,应该开始检查用户是否遵守了协议并实现了协议方法,从而决定是否需要创建push动画。

当检测到用户需要push动画的时候,我们就要开始准备push动画了。我们把pop动画交给系统的时候,是需要把根导航控制器(JPNavigationController)的delegate置为nil的,并且需要为我们自定义的UIPanGestureRecognizer添加target,这个我在第一篇文章已经讲过了。由于pop已经交给系统处理,所以这里只负责处理push动画。系统是没有push动画的,所以我们要自己动手来实现。要想代理系统的push动画,我们需要成为根导航控制器(JPNavigationController)的代理,遵守协议,并且实现两个require的代理方法。

我们在第一个方法里检查是否是push操作,如果是,我们就要返回我们自定义的push动画对象。同时,我们需要手势驱动动画过程,所以,我们需要创建手势监控者来负责在用户滑动的时候更新动画,也就是第二个方法。

创建手势监控者的代码如下:

5.4. 跟踪用户的手势,驱动过渡动画

还记得上面那个左滑push的动画吗?你可能觉得和系统默认的pop动画相比,就是把系统的pop动画反过来,就成了push动画了。如果你能这么想,那恭喜你,你的直觉很对!!其实,我们很多时候做很多东西都是在模仿系统的实现,在猜系统这个效果究竟是怎么实现的,然后再一步一步验证我们的想法是否正确。

142122663-86ca02d6f190a21a

当你打开我的demo运行的时候,你看到的是左边的那个样子,现在我告诉你,实际上它的图层关系是右边的这个样子。也就说,在用户左滑的那一刻我们需要将准备右图做动画需要的元素,包括当前控制器的View的截图B,要push到的控制器的View的截图C,然后把它们按照这个图层关系添加到系统提供给我们用来做动画的容器中。再在动画提供者中告诉系统,我们需要做动画的两个元素B和C在动画起始的时候的frame,以及在动画终点的时候这两个元素的frame。这个手势驱动的过程,因为我们已经把这个监听过程交给手势监控者,并返还给系统处理了,所以这个过程系统会帮我们处理好。

但是问题是,为什么我们要用截图的方式,而不是直接用两个控制器的View来做动画?这么做的原因就是,当当前窗口有显示tabBar的时候,tabBar图层是在动画容器图层之上的,所以我们无法优雅的做百分手势驱动。所以采取这种方式。但是系统的pop手势不是用截图的形式,而是直接使用两个控制器的View来做动画,就像下面这样,但是由于权限问题,我们不可能像系统那样做,但是也不排除有同学想到巧妙的办法来实现。

152122663-5fc072790b6345ec

下面看下动画提供者的源码:

5.5. 完成push手势

到了这里,基本上已经完成了push功能了。只需要在手势结束的时候告诉系统,是push成功还是失败就可以了。

我的文章集合

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

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

相关文章

可能感兴趣的话题



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