随着技术的迭代,现在对App的效果要求越来越高,那么在这篇文章里面我们一起讨论一下如何在控制器做跳转的时候对导航栏做平滑过渡的转场
构思:
- 首先要获取到导航栏里的子控件来设置其透明度 (实现透明度变化)
- 为所有控制器添加一个导航栏透明度属性,用于记录当前控制器的导航栏透明度 (记录透明度值)
- 通过监听手势滑动来获取源和目的控制器,计算从源到目的控制器的透明度变化,来改变导航栏的透明度 (实现平滑过渡)
1、实现透明度变化
要想实现透明度变化,得先获取到导航栏里的子控件,然后设置其alpha值。但是如何获取呢? 首先考虑使用KVC,通过导航栏的 valueForKey: 方法来获取子控件对象,但是在不同的系统上,导航栏里的子控件布局排布也是有所不同, 意味着key值并非固定,通过key值拿子控件对象的方法在不同的系统上就很容易抛异常。
考虑到这一点,我采用了最直接的方式:遍历导航栏的所有子控件,拿到首个子控件给其设置透明度。这样不但不需要再去考虑系统的问题了,同时也能满足带颜色的导航栏或者是带背景图的导航栏透明度的变化。
- (void)xa_changeNavBarAlpha:(CGFloat)navBarAlpha{ |
2、记录透明度
每个控制器都应该有自己的导航栏透明度且当透明度发生变化后我们都应该把值保存下来,以方便下次的使用,这里我们就给UIViewController添加一个分类并加一个navBarAlpha的属性,这样我们就可以直接通过控制器去设置导航栏的透明度啦~
- (CGFloat)xa_navBarAlpha{ |
3、实现平滑过渡
现在存在的问题就是我在某个页面设置了导航栏的透明度,back回上一个界面,导航栏的透明度值仍然是上个界面的
这里做过渡有两种情况,一种是手势滑动back回上一个界面,还有一种情况是直接点击了back按钮回到上一个界面的。根据这两种情况我们分别做一下处理。
3.1、手势滑动back
这种情况我们需要去监听导航控制器的手势滑动,导航控制器有个方法 _updateInteractiveTransition: ,该方法可以监听手势滑动以及当前转场的进度,我们可以通过swizzing来交换方法实现,来接手 _updateInteractiveTransition: 方法调用的监听
+ (void)load{ |
然后通过转场的上下文信息,拿到源和目的的控制器navBarAlpha值,再根据percentComplete转场进度参数计算并设置导航栏透明度值,这样就完成了手势滑动的back
- (void)xa_updateInteractiveTransition:(CGFloat)percentComplete{ |
3.2、按钮点击back
当点击buttonItem back控制器的时候我们可以在 viewWillAppear: 的时候设置回当前控制器的透明度值,所以我们同样要交换 viewWillAppear: 方法的实现,那么每当控制器要显示的时候,我们总是要将它重置回当前控制器应有的透明度值。
这里另外还需要做两个逻辑判断:
- 一个是判断手势是否正在滑动。如果是YES表示当前的状态是手势滑动back的状态则不需要处理。
- 另外一个逻辑是判断当前控制器是否设置过navBarAlpha的属性值。如果有设置过,那么每次控制器要显示的时候都要将导航栏透明度设置成控制器储存的透明值。反之,我们给这个控制器设置一个默认的透明度值
- (void)xa_viewWillAppear:(BOOL)animated{ |
4、细节优化与调整
在手势滑动的时候,如果滑动到了一半就松手了,那么导航栏就可能自动完成或者取消返回操作了,导致剩下的导航栏的透明度将无法计算,可以看到firstViewController的导航栏透明度并非是1
对于这一点的话,我们可以添加手势滑动的交互的状态,如果当前的滑动的过程中中断,那么判断是取消操作还是完成操作,然后再完成剩余的动画效果。
首先我们要去监听导航控制器的 popViewControllerAnimated: 的方法调用
+ (void)load{ |
然后再通过转场协调器对象监听手势滑动交互的改变
- (UIViewController *)xa_popViewControllerAnimated:(BOOL)animated{ |
最后我们通过转场的上下文信息根据操作(自动完成还是返回操作)来获取剩余的动画时长,并完成剩余的动画
- (void)dealNavBarChangeAction:(id<UIViewControllerTransitionCoordinatorContext>)context { |
最后:
下期会写一篇全屏pop手势的文章,可以配合该项目一起使用
demo的地址,欢迎star喔~🤗