听过好多次:“程序员要通过多读好的源码来提升自己”这样类似的话,而且又觉得自己有很多不会的,于是就马上启动了自己的读好源码Project。
从哪个框架开始呢?我想到了SDWebImage
,但是大致看下来文件很多,代码也不少,不知道从何看起,于是作罢。所以茅塞顿开,还是从最最简单的框架开始吧~因为学习曲线要给自己设定得平缓一点才有利于稳步提升,小步快跑才是王道~
找着找着就找到了MBProgressHUD
,这个框架只有两个文件,一个头文件和一个实现文件,很适合我现在的水平(对于一个没怎么读过源码的选手),于是就撸起了袖子开始了。
连查知识点带记笔记一共花了大概3个小时(虽然文件很少,但是里面好多东西都不知道[捂脸])。整体说来,收获还是比较大的,除了一些零碎的语法之外,框架作者对于代码结构的设计和各种情况的考虑还是很出色的,很值得学习,而且我在下文也有介绍。
这篇总结主要分三个部分来介绍这个框架:
- 核心Public API
- 方法调用流程图
- 方法内部实现
不多说了,开始吧~
1. 核心Public API
属性:
1 | @property (assign, nonatomic) MBProgressHUDMode mode;//HUD的类型 |
类方法:
1 | /** |
对象方法:
1 | /** |
看完了这些比较主要的API,我们看一下方法调用的流程图:
2. 方法调用流程图:
总体来说,这个第三方框架的接口还是比较整齐的,可以大致上分为两类:显示(show)和隐藏(hide)。而且无论是调用显示方法还是隐藏方法,最终都会走到私有方法animateIn:withType: completion:
里(前提是附加动画效果)。可以看一下方法调用的流程图:
看完方法调用的结构之后,我们来具体看一下方法内部是如何实现的:
3. 方法内部实现:
在讲解API之前,有必要先介绍一下HUD使用的三个Timer。
1 | @property (nonatomic, weak) NSTimer *graceTimer; //执行一次:在show方法触发后到HUD真正显示之前,前提是设定了graceTime,默认为0 |
- graceTimer:用来推迟HUD的显示。如果设定了graceTime,那么HUD会在
show
方法触发后的graceTime时间后显示。它的意义是:如果任务完成所消耗的时间非常短并且短于graceTime,则HUD就不会出现了,避免HUD一闪而过的差体验。 - minShowTimer:如果设定了minShowTime,就会在
hide
方法触发后判断任务执行的时间是否短于minShowTime。因此即使任务在minShowTime之前完成了,HUD也不会立即消失,它会在走完minShowTime之后才消失,这应该也是避免HUD一闪而过的情况。 - hideDelayTimer:用来推迟HUD的隐藏。如果设定了delayTime,那么在触发
hide
方法后HUD也不会立即隐藏,它会在走完delayTime之后才隐藏。
这三者的关系可以由下面这张图来体现(并没有包含所有的情况):
下面开始分别讲解show
系列的方法和hide
系列的方法。
show系列方法
1 | + (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { |
我们可以看到,无论是类方法的show方法,还是对象方法的show方法,而且无论是触发了
graceTimer
还是没有触发,最后都会走到showUsingAnimation:
方法来让HUD显示出来。
这里补充讲解一下NSProgress的监听方法:
1 | - (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled { |
CADisplayLink是一个能让我们以和屏幕刷新率同步的频率将特定的内容画到屏幕上的定时器类。 CADisplayLink以特定模式注册到runloop后, 每当屏幕显示内容刷新结束的时候,runloop就会向 CADisplayLink指定的target发送一次指定的selector消息, CADisplayLink类对应的selector就会被调用一次。
参考文章:Core Animation系列之CADisplayLink
hide系列方法
1 | + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { |
我们可以看到,无论是类方法的
hide
方法,还是对象方法的hide
方法,而且无论是触发还是没有触发minShowTimer
,最终都会走到hideUsingAnimation
这个方法里。
而无论是show
方法,还是hide
方法,在设定animated属性为YES的前提下,最终都会走到animateIn: withType: completion:
方法:
1 | - (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion { |
除了一些细节上的语法之外,我觉得该框架有几个地方值得我们借鉴:
- 暴露出来的API最终都会走到同一个私有方法里。
- 将真正显示的时间的前后加上缓冲的时间(graceTimer 和 hideDelayTimer),可以提高可定制性和稳定性。
- 如果有两个方法是矛盾的,并且可以同时调用,就需要在全局设置一个属性来判断当前的状态(removeFromSuperViewOnHide属性,finished属性)
- 使用CADisplayLink来刷新更新频率可能很高的view。
- 使用NSAssert来捕获各种异常。
就这样大致写完了,没有怎么读过第三方框架的源码,所以第一次可能显得稍许不足。有不好的地方还希望多多指点哈~
————————————————- 2018年7月17日更新 ————————————————-
注意注意!!!
笔者在近期开通了个人公众号,主要分享编程,读书笔记,思考类的文章。
- 编程类文章:包括笔者以前发布的精选技术文章,以及后续发布的技术文章(以原创为主),并且逐渐脱离 iOS 的内容,将侧重点会转移到提高编程能力的方向上。
- 读书笔记类文章:分享编程类,思考类,心理类,职场类书籍的读书笔记。
- 思考类文章:分享笔者平时在技术上,生活上的思考。
因为公众号每天发布的消息数有限制,所以到目前为止还没有将所有过去的精选文章都发布在公众号上,后续会逐步发布的。
而且因为各大博客平台的各种限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~
扫下方的公众号二维码并点击关注,期待与您的共同成长~