骨架屏Git地址(https://github.com/tigerAndBull/TABAnimated)
在看源码分析前,看一下Git上的ReadMe和Documents下的文档,最好能够使用过TABAnimated。
TABAnimated 目录结构
TABAnimated
-Core
-TABAnimated.h (全局属性配置等)
-Cache (缓存模块)
-Animation (添加动画模块)
-Category (为View添加TAB管理和动画的开启关闭等模块)
-Manager (骨架的生成、配置、管理)
-Model(View、TableView、CollectionView初始化对象)
-Reveal (输入条件,实时查看代码的工具)
分析模块
本文不对Reveal和Cache进行分析。
Reveal没有仔细使用。Cache原理可以参考作者的原文:缓存策略
对于骨架屏的生成原理,View、TableView、Collection基本流程都是一样的。先只针对TableView进行探索吧。
一句话解释TABAnimated原理
利用Runtime交换方法原理,交换TableView的Delegate、DataSoure代理的相关方法成框架内的tab_开头的方法,在cellForRow的方法中,遍历cell的subViews,通过UIView的convertRect:fromView方法得到相关subview的CGRect,然后使用贝塞尔曲线和CAShapeLayer构成骨架,添加到layer层上,完成骨架的构建。
按流程解析源码
初始化
_tableView.tabAnimated = [TABTableAnimated animatedWithCellClass:[TestTableViewCell class]
cellHeight:90];
1.通过Category文件下的UIView+TABAnimated.h 为UITablView添加了tabAnimated属性(runtime添加),然后使用了Model文件下的TAbTableAnimated.h的tablview的TAb初始化方法。
2.传入的参数主要用于tab_开头的tbalview代理的实现。[cell class]和cell height嘛。
开启骨架屏
[tableView tab_startAnimation];
1.tab_startAnimation 是Category文件下UIView+TABControlAnimation.h的方法,该文件主要控制骨架屏的显示和关闭等操作事件。
2.tab_startAnimation中判断了一下动画状态,然后调用了
- (void)startAnimationIsAll:(BOOL)isAll
index:(NSInteger)index
该方法中,对View、TableView、CollectionView进行了分别的判断处理,针对TableView,首先通过
[tabAnimated exchangeTableViewDelegate:tableView];
[tabAnimated exchangeTableViewDataSource:tableView];
对tableview的Delegate和DataSource进行了方法交换。然后遍历了初始化时传入的[cell class] array 对tablview进行cell的register,接着处理预估高度和headerView、footerView,最后查看一下是否有指定row显示骨架的需求,然后reload tablview。
3.reload之后,因为交换了方法,所以会走tab_开头的代理方法。相关代理方法在TABTableAnimated.m文件中。
主要看一下tab_tableView:cellForRowAtIndexPath:方法
首先通过runMode属性确定了index。根据index判断section或者row是否需要显示骨架
if ([tableView.tabAnimated currentIndexIsAnimatingWithIndex:index]) {
...
}
return [self tab_tableView:tableView cellForRowAtIndexPath:indexPath];
,需要则进入if,不需要则
return [self tab_tableView:tableView cellForRowAtIndexPath:indexPath];
显示原来的tablview的界面。在if中,通过cell class 的array 取得Tableviewcell。然后为cell 初始化tabComponentManager属性
[TABManagerMethod fullData:cell];
fullData方法为subView添加假数据,调整骨架图形的位置。
最后通过
[TABManagerMethod runAnimationWithSuperView:tableView
targetView:weakCell
isCell:weakCell
manager:weakCell.tabComponentManager];
显示骨架。
TABManagerMethod
上一步中调用了runAnimationWithSuperView这个方法显示了骨架。
在这个方法中,首先根据状态确定是要显示骨架,还是结束骨架显示。
在显示骨架的if分支中,有三个主要的方法
//1.
[TABManagerMethod getNeedAnimationSubViews:targetView
withSuperView:superView
withRootView:targetView
withRootSuperView:superView
isInNestView:NO
array:array];
//2.
[targetView.tabComponentManager installBaseComponentArray:array.copy];
//3.
[targetView.tabComponentManager updateComponentLayers];
在第一个方法中,通过递归,为每个subview都生成了layer加入到array中, 在递归过程中,对于一些特殊的和需要过滤的视图,添加了过滤标记needRemove。通过needRemove为layer的loadStype标记为Remove,在第三个方法添加的时候会跳过。
在第二个中,将第一个方法生成的layer的array赋值给manager的componentLayerArray,和生成BaseComponentArray
在第三个方法中,把BaseComponentArray中的对象持有的layer添加到tabLayer上,显示出来
至此 显示逻辑基本结束
关闭骨架屏
[self.tableView tab_endAnimationEaseOut];
通过这个方法,修改动画状态,并且将runAnimationIndexArray移除所有元素,重新reload显示原本的tableview;
[tabAnimated.runAnimationIndexArray removeAllObjects];
因为在tab_tableview:cellForRow
方法中,通过
[tableView.tabAnimated currentIndexIsAnimatingWithIndex:index]
进行了if 分支判断当runAnimationIndexArray没有元素时,会执行原本的tableview:cellForRowAtIndexPath.
未分析模块
- 缓存模块
- collectionView、View模块(流程应该差不多和tablview)
- 链式语法模块
- 动画模块(闪光灯,豆瓣动画等)
网友评论