写在前面
滑动导航的问题,主要是解决上方TabBar与下方ScrollView的联动问题。
在上一篇文章中,我们详细分析了ScrollView构成。
本篇我们就来看看作者是怎么处理联动问题的
一、TYTabPagerController,TYTabButtonPagerController总体介绍
TYTabPagerController在基类的基础上,主要做了两件事情:
- 增加头部CollectionView
- 增加滑动过程中的逻辑处理
而TYTabButtonPagerController在TYTabPagerController的基础上,也做了两件事情:
- 增加了Label Cell至CollectionView(头部tabBar)
- 实现了 TYTabPagerControllerDelegate协议的部分方法
二、TYTabPagerController详细分析
先看一个效果图:
重点看一下头部的tabBar,主要由两个部分组成:
- 标签
- UnderView
标签部分,我们可以用Label+ScrollView的方式实现,也可以直接使用CollectionView,推荐使用后者(为我们封装好了很多现成的功能,下文会提到)。
UnderView部分,根据Index和滑动比实时计算其frame就可以实现。
下面我们看看作者具体是怎么写得。
2.1 init
一如既往,只是简单的配置了下数据:
2.2 life Cycle
这部分,主要是各部分视图初始化,大体过程:
2.3 代理
2.3.1 TYTabPagerControllerDelegate
继承自基类中定义的TYPagerControllerDelegate,也是用来处理transition逻辑的:
2.3.2 setDelegate
在设置代理的时候,作者用一个struct记录了代理是否实现了相关方法,避免了后面冗余的检查代码,值得学习。
2.3.3 collectionViewDelegate
2.4 滑动逻辑处理(重点)
在上一篇中,我们已经讨论过了滑动逻辑处理的总体计算流程:
TYTabPagerController主要实现了上图中提到的,子类、代理实现的处理index以及progress的方法。
2.4.1 处理Index的方法
作用:在本方法中,调用在一次滑动过程中只需要处理一次的方法。
举例:通知delegate刚刚完成一次transition,或者调整collectionView的offset使选中Item居中显示
看看上面的流程图,回忆一下基类中的流程(只有当index发生改变的时候,该处理方法才会被调用)。
所以特别适合处理一次滑动只需要响应一次的事件,比如下图的选中Label自动居中的功能:
该功能的实现,是collectionView为我们封装好的:
注意上图我的注释,『如果可能的话』,什么情况下才会生效?
起始这个功能的实现,本质上就是对CollectionView Offset的修改,而Offset是有范围的,很明显不能为了居中显示指定的Item,将Offset超范围修改。
所以,在左侧和右侧的Item被选中的时候,CollectionView的offset是不会被修改的。
2.4.2 处理Progress的方法
作用:在此方法中调用需要实时处理的方法
举例:修改underLineView的Frame,修改Label的transform、color等一切需要跟Progress实时互动的事件
在该方法中,调用了delegate处理progress相关的方法;
还调用了另一个核心方法:setUnderLineFrame
setUnderLineFrame
作用:实时设定underLineViewFrame
实现:根据toIndex、FromIndex和滑动比实时计算UnderViewFrame
难点:不同风格对应不同的计算公式
上面这一坨代码,是不是看起来很晕?别急,其实拆开来看,并不难,我们下面一一进行分析:
先指明一下作者设定的一个的变量:
这个属性的作用呢,就是指定underLineView的首尾距离与Cell首尾的距离的,特此说明一下方便理解。
一般风格TYPagerBarStyleProgressView
我们从最简单也是最常用的部分开始介绍:
为了便于分析,我们先假设underLineView的width与cell的width一致。
先分析originX:
- 初始值:fromCell.frame.origin.x
- 目标值:toCell.frame.origin.x
- 变化值:(toCell.frame.origin.x - fromCell.frame.origin.x)
将变化值与我们的progress挂钩,就可以得到动态变化的width了。
按照我们的分析来看一下源代码。
图中标号为1的部分,toCellFrame.origin.x + targetUnderLineViewEdgeToCell,其实就是我们上面提到的目标值。
图中标号为2的部分,就是我们的初始值。
两者相减 X progress 就是我们需要的与Progress挂钩的动态变化值。
再加上标号3,我们的初始值,就得到了frame.origin.x的计算式。
再看width部分,一样的分析方法:
- 初始值: fromCell.frame.width
- 目标值: toCell.frame.width
- 变化值: (toCell.frame.width - fromCell.frame.width)
- 计算表达式:fromeCell.frame.width + (toCell.frame.width - fromCell.frame.width) X progress
原作者的表达式化简一下,会得到一样的式子。
Bounce风格TYPagerBarStyleProgressBounceView
Bounce风格的话,比常见的要麻烦一些,不过列计算表达式的方式还是一样的:
- 初始值
- 目标值
- 变化值
只要找出这三者,就可以列出符合要求的表达式。
这个动画过渡,是以progress=0.5为分界,分成上下两个部分,我们先来看上半部分的动画:
X坐标:
- 初始值:fromCellFrame.origin.x + fromEdge
- 目标值:同初始值
- 变化值:0
从动画中,我们也可以看到,这个阶段的originX一致保持初始值的状态。
width:
- 初始值: fromeCellFrame.width - 2*fromEdge
- 目标值:初始值 + fromEdge + cellSpacing + toEdge + toCellFrame.width - 2 *toEdge
- 变化值: 目标值 - 初始值 = fromEdge + cellSpacing + toEdge + toCellFrame.width - 2 *toEdge
- 表达式: 初始值 + 变化值progress2(这里之所以x2,是因为progress的变化范围为[0,5],而我们需要的变化区间为[0,1])
我们来看一下作者的源码:
图标1处即为初始值,图标2处为变化值,最后变化值与2*progress相乘。
小结
分析方法就是这么简单,找出初始值、目标值、变化值,再确认我们的变化区间,就可以写出我们的目标表达式。
其它情况的分析方法也一样,这里就不再赘述了,有兴趣的朋友可以自己推导一下。
三、TYTabButtonPagerController分析
这个类,是作者为我们写好的一个滑动导航控制器,基本能满足我们的日常需求。
它主要是在TYTabPagerController的基础上,对一些细节进行了设置,总体没有什么难度,所以就不细讲了,感兴趣的同学可以自己看一下。
核心逻辑处理部分就一处:
与上一节的setUnderLineViewFrame同时进行,对Cell的颜色以及字体进行变换,代码很简单。
四、总结
至此,滑动导航控制器的第三方库分析已经完成了。
TYPagerController基本能满足我们的日常需求,当我们有更进一步的需求的时候,也可以再进行定制。
整体来说,代码的逻辑控制不错,要是变量名、方法名还有注释能更完善一些就更好了。
读起来还是有些不顺畅,不过也算是给自己提个醒:
『代码是写给人看的』。