源码解析--MBProgressHUD
2017-08-21 本文已影响0人
东了个尼
读者需知
最近和同事讨论如何快速提升自己的开发技术,以及一些项目中的设计思维,最后大家得出一个结论,就是阅读优秀的开源框架,因为这些开源框架中,往往包含了很多优秀的设计思维,而且设计又更加的巧妙和规范,所以阅读源码对于一个初学者来说,无疑是一个绝佳的学习途径。建议很多初学者在刚开始学习的时候,可以找一些简单的框架学习,如果一开始就去看功能复杂,设计复杂的框架,往往很难看得懂,这样容易受到打击,丧失继续阅读下去的兴趣,在这里推荐一个开源框架,同时也是经常用到的一个第三方框架,MBProgressHUD。MBProgressHUD是一个非常受欢迎的第三方库,其用法简单,代码朴实易懂,涉及的知识点广而不深奥,是非常适合初学者阅读的一份源码。
下面开始正题
//1.设置枚举的类型 MBProgressHUDMode 显示的模式 UIActivityIndicatorView默认是小菊花
typedef NS_ENUM(NSInteger, MBProgressHUDMode) {
/// UIActivityIndicatorView
MBProgressHUDModeIndeterminate,
/// A round, pie-chart like, progress view.
MBProgressHUDModeDeterminate,
/// Horizontal progress bar.
MBProgressHUDModeDeterminateHorizontalBar,
/// Ring-shaped progress view.
MBProgressHUDModeAnnularDeterminate,
/// Shows a custom view.
MBProgressHUDModeCustomView,
/// Shows only labels.
MBProgressHUDModeText
};
2.动画类型
//MBProgressHUDAnimation 枚举表示动画类型 默认是 MBProgressHUDAnimationFade
typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) {
/// Opacity animation
MBProgressHUDAnimationFade,
/// Opacity + scale animation (zoom in when appearing zoom out when disappearing)
MBProgressHUDAnimationZoom,
/// Opacity + scale animation (zoom out style)
MBProgressHUDAnimationZoomOut,
/// Opacity + scale animation (zoom in style)
MBProgressHUDAnimationZoomIn
};
3.背景View的类型
//MBProgressHUDBackgroundStyle 枚举表示背景类型 默认是MBProgressHUDBackgroundStyleSolidColor(纯色)
typedef NS_ENUM(NSInteger, MBProgressHUDBackgroundStyle) {
/// Solid color background
MBProgressHUDBackgroundStyleSolidColor,
/// UIVisualEffectView or UIToolbar.layer background view
MBProgressHUDBackgroundStyleBlur
};
2. MBProgressHUD主要API介绍
//1.创建实例并且添加到对应的view上
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
//创建MBProgressHUD对象
MBProgressHUD *hud = [[self alloc] initWithView:view];
//设置 隐藏的时候从父视图删除
hud.removeFromSuperViewOnHide = YES;
[view addSubview:hud];
//设置动画类型
[hud showAnimated:animated];
return hud;
}
2.//隐藏hud
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
具体实现
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [self HUDForView:view];
判断hud是否为空,如果为空返回nil,如果不为空设置隐藏的时候,从父视图移除
if (hud != nil) {
hud.removeFromSuperViewOnHide = YES;
[hud hideAnimated:animated];
return YES;
}
return NO;
}
+ (MBProgressHUD *)HUDForView:(UIView *)view {
//倒序排列 subviews
NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
//遍历所有的子视图
for (UIView *subview in subviewsEnum) {
//判断是否已经含有MBProgressHUD类型的子控件
if ([subview isKindOfClass:self]) {
MBProgressHUD *hud = (MBProgressHUD *)subview;
//判断动画是否结束
if (hud.hasFinished == NO) {
return hud;
}
}
}
return nil;
}
3.显示动画
#pragma mark - Show & hide
- (void)showAnimated:(BOOL)animated {
//主线程宏定义
MBMainThreadAssert();
//设置定时器无效
[self.minShowTimer invalidate];
self.useAnimation = animated;
//设置finished为NO表示没有结束
self.finished = NO;
// If the grace time is set, postpone the HUD display
if (self.graceTime > 0.0) {
//此种方式创建的timer没有添加至runloop中
NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
//将定时器添加到runloop中(//保持线程为活动状态,才能保证定时器执行)
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.graceTimer = timer;
}
// ... otherwise show the HUD immediately
else {
[self showUsingAnimation:self.useAnimation];
}
}
- (void)showUsingAnimation:(BOOL)animated {
// Cancel any previous animations
//移除self.bezelView.layer上的所有动画操作
[self.bezelView.layer removeAllAnimations];
//移除self.backgroundView.layer上的所有动画操作
[self.backgroundView.layer removeAllAnimations];
// Cancel any scheduled hideDelayed: calls
[self.hideDelayTimer invalidate];
self.showStarted = [NSDate date];
self.alpha = 1.f;
// Needed in case we hide and re-show with the same NSProgress object attached.
[self setNSProgressDisplayLinkEnabled:YES];
if (animated) {
[self animateIn:YES withType:self.animationType completion:NULL];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
self.bezelView.alpha = self.opacity;
#pragma clang diagnostic pop
self.backgroundView.alpha = 1.f;
}
}
4.隐藏动画
隐藏动画
- (void)hideAnimated:(BOOL)animated {
MBMainThreadAssert();
[self.graceTimer invalidate];
self.useAnimation = animated;
//设置结束为yes
self.finished = YES;
// If the minShow time is set, calculate how long the HUD was shown,
// and postpone the hiding operation if necessary
if (self.minShowTime > 0.0 && self.showStarted) {
//动画开始的时间距离现在的时间间隔
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
//判断动画是否已经执行完成
if (interv < self.minShowTime) {
//如果动画没有执行到指定的时间,继续执行下去,计算剩余的时间
NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.minShowTimer = timer;
return;
}
}
// ... otherwise hide the HUD immediately
[self hideUsingAnimation:self.useAnimation];
}
- (void)hideUsingAnimation:(BOOL)animated {
//判断是否在执行动画
if (animated && self.showStarted) {
self.showStarted = nil;
[self animateIn:NO withType:self.animationType completion:^(BOOL finished) {
//设置动画完成
[self done];
}];
} else {
self.showStarted = nil;
//隐藏bezelView
self.bezelView.alpha = 0.f;
self.backgroundView.alpha = 1.f;
//设置动画完成
[self done];
}
}
5.判断动画类型
- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
// Automatically determine the correct zoom animation type
//判断动画类型是不是MBProgressHUDAnimationZoom
if (type == MBProgressHUDAnimationZoom) {
type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut;
}
//设置动画缩放比例50%
CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f);
//设置动画缩放比例150%
CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f);
// Set starting state
UIView *bezelView = self.bezelView;
//当动画开始并且bezelView透明的时候,且动画类型是MBProgressHUDAnimationZoomIn的时候
if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) {
bezelView.transform缩小50%
bezelView.transform = small;
} else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) {
//当动画开始并且bezelView透明的时候,且动画类型是MBProgressHUDAnimationZoomOut的时候
bezelView.transform放大150%
bezelView.transform = large;
}
// Perform animations
dispatch_block_t animations = ^{
if (animatingIn) {
bezelView.transform = CGAffineTransformIdentity;
} else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) {
bezelView.transform = large;
} else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) {
bezelView.transform = small;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
bezelView.alpha = animatingIn ? self.opacity : 0.f;
#pragma clang diagnostic pop
self.backgroundView.alpha = animatingIn ? 1.f : 0.f;
};
// Spring animations are nicer, but only available on iOS 7+
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) {
[UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
return;
}
#endif
[UIView animateWithDuration:0.3 delay:0. options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
}
6.操作完成
- (void)done {
// Cancel any scheduled hideDelayed: calls
//设置隐藏定时器为空
[self.hideDelayTimer invalidate];
[self setNSProgressDisplayLinkEnabled:NO];
如果已经结束了
if (self.hasFinished) {
//设置self.alpha为空
self.alpha = 0.0f;
//判断如果self.removeFromSuperViewOnHide属性为YES
if (self.removeFromSuperViewOnHide) {
//移除自己
[self removeFromSuperview];
}
}
MBProgressHUDCompletionBlock completionBlock = self.completionBlock;
·//执行block
if (completionBlock) {
completionBlock();
}
触发代理方法
id<MBProgressHUDDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
[delegate performSelector:@selector(hudWasHidden:) withObject:self];
}
}
有一些好的设计,以后在自己的设计中可以效仿。
1.使用枚举做类型判断,区分不同的种类(枚举作为API的接口参数)
- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
2.使用更多的BOOL值在设计中去判断不同的状态
@property (nonatomic, assign) BOOL useAnimation;
@property (nonatomic, assign, getter=hasFinished) BOOL finished;