源码解析--MBProgressHUD

读者需知

最近和同事讨论如何快速提升自己的开发技术,以及一些项目中的设计思维,最后大家得出一个结论,就是阅读优秀的开源框架,因为这些开源框架中,往往包含了很多优秀的设计思维,而且设计又更加的巧妙和规范,所以阅读源码对于一个初学者来说,无疑是一个绝佳的学习途径。建议很多初学者在刚开始学习的时候,可以找一些简单的框架学习,如果一开始就去看功能复杂,设计复杂的框架,往往很难看得懂,这样容易受到打击,丧失继续阅读下去的兴趣,在这里推荐一个开源框架,同时也是经常用到的一个第三方框架,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 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;

你可能感兴趣的:(源码解析--MBProgressHUD)