源码解析--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<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;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,035评论 25 707
  • 起源 Web的安全模型根植于同一源策略。 跨站点脚本(XSS)攻击可绕过同源策略。 内容安全策略(Content-...
    从此以后dapeng阅读 985评论 0 0
  • 双十一卖了1482亿,马云又该笑了。这一天,不知有多少人彻夜未眠,为它疯狂:买买买,剁手也要买…… ...
    俞见阅读 222评论 0 0
  • 春游 三月云开春料峭 桃红柳绿尽显妖 红杏枝头报春急 玉兰海棠竞嫽俏 蜂勤问花寻蜜意 佳人沈醉花间照 莺雀鸣...
    柳风月色阅读 191评论 0 0