源码阅读计划——MBProgressHUD源码解析

MBProgressHUD是一个优秀的弹窗提示的一个开源框架,项目中几乎都会用到。

一、先看一下该弹窗的显示模式:

typedef NS_ENUM(NSInteger, MBProgressHUDMode) {
    /// 默认模式,使用系统自带的菊花
    MBProgressHUDModeIndeterminate,
    /// 带饼图进度条
    MBProgressHUDModeDeterminate,
    /// 带条形进度条
    MBProgressHUDModeDeterminateHorizontalBar,
    /// 圆环进度条
    MBProgressHUDModeAnnularDeterminate,
    /// 自定义
    MBProgressHUDModeCustomView,
    /// 纯文本
    MBProgressHUDModeText
};

1、带加载菊花的弹窗

2、带菊花和文字的弹窗

3、带进度条和文字的弹窗

4、带进度条文字和取消按钮的弹窗

5、带条形进度条和文字的弹窗

6、普通的toast弹窗

7、带文字和图片的弹窗


以上样式几乎可以适用大部分的应用场景了,除此以外MBProgressHUD还支持自定义样式

二、用法

+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;
- (instancetype)initWithView:(UIView *)view;
- (void)showAnimated:(BOOL)animated;
- (void)hideAnimated:(BOOL)animated;
- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay;

通过头文件可以看出使用方式有两种,一种的类函数,另一种是直接init初始化调用显示

三、MBProgressHUD的层次结构

/// 最外层的半透明背景
@property (strong, nonatomic, readonly) MBBackgroundView *backgroundView;
/// 中间的圆角矩形
@property (strong, nonatomic, readonly) MBBackgroundView *bezelView;
/// 自定义View
@property (strong, nonatomic, nullable) UIView *customView;
/// 文本
@property (strong, nonatomic, readonly) UILabel *label;
/// 详情文本
@property (strong, nonatomic, readonly) UILabel *detailsLabel;
/// 按钮
@property (strong, nonatomic, readonly) UIButton *button;

四、初始化函数

- (instancetype)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        [self commonInit];
    }
    return self;
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if ((self = [super initWithCoder:aDecoder])) {
        [self commonInit];
    }
    return self;
}

- (id)initWithView:(UIView *)view {
    NSAssert(view, @"View must not be nil.");
    return [self initWithFrame:view.bounds];
}
由此看出所有的初始化调用最终都会走到commonInit
- (void)commonInit {
    // 设置默认属性
    _animationType = MBProgressHUDAnimationFade;
    _mode = MBProgressHUDModeIndeterminate;
    _margin = 20.0f;
    _opacity = 1.f;
    _defaultMotionEffectsEnabled = YES;

    // 设置默认颜色
    BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
    _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f];
    // 背景颜色透明
    self.opaque = NO;
    self.backgroundColor = [UIColor clearColor];
    // 先隐藏自己
    self.alpha = 0.0f;
    self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    self.layer.allowsGroupOpacity = NO;

    [self setupViews];
    [self updateIndicators];
    [self registerForNotifications];
}

setupViews函数这里把主要的backgroundView、bezelView初始化并且加入MBProgressHUD的子view里面,且把label、detailLabel、button都添加进bezelView里面了

- (void)setupViews {
    UIColor *defaultColor = self.contentColor;

    MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds];
    …
    _backgroundView = backgroundView;

    MBBackgroundView *bezelView = [MBBackgroundView new];
    …
    _bezelView = bezelView;
    [self updateBezelMotionEffects];

    UILabel *label = [UILabel new];
    …
    _label = label;

    UILabel *detailsLabel = [UILabel new];
    …
    _detailsLabel = detailsLabel;

    UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom];
    …
    _button = button;

    for (UIView *view in @[label, detailsLabel, button]) {
        view.translatesAutoresizingMaskIntoConstraints = NO;
        [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
        [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
        [bezelView addSubview:view];
    }

    UIView *topSpacer = [UIView new];
    topSpacer.translatesAutoresizingMaskIntoConstraints = NO;
    topSpacer.hidden = YES;
    [bezelView addSubview:topSpacer];
    _topSpacer = topSpacer;

    UIView *bottomSpacer = [UIView new];
    bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO;
    bottomSpacer.hidden = YES;
    [bezelView addSubview:bottomSpacer];
    _bottomSpacer = bottomSpacer;
}

updateIndicators根据mode创建相应的进度条,如果是MBProgressHUDModeCustomView模式则会创建自定义的View

- (void)updateIndicators {
    UIView *indicator = self.indicator;
    BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
    BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];

    MBProgressHUDMode mode = self.mode;
    if (mode == MBProgressHUDModeIndeterminate) {
        if (!isActivityIndicator) {
            // 带菊花的进度条
            [indicator removeFromSuperview];
            indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
            [(UIActivityIndicatorView *)indicator startAnimating];
            [self.bezelView addSubview:indicator];
        }
    }
    else if (mode == MBProgressHUDModeDeterminateHorizontalBar) {
        // 条形进度条
        [indicator removeFromSuperview];
        indicator = [[MBBarProgressView alloc] init];
        [self.bezelView addSubview:indicator];
    }
    else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) {
	// 饼状和环形进度条
        if (!isRoundIndicator) {
            [indicator removeFromSuperview];
            indicator = [[MBRoundProgressView alloc] init];
            [self.bezelView addSubview:indicator];
        }
        if (mode == MBProgressHUDModeAnnularDeterminate) {
            [(MBRoundProgressView *)indicator setAnnular:YES];
        }
    } 
    else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) {
        // 自定义View
        [indicator removeFromSuperview];
        indicator = self.customView;
        [self.bezelView addSubview:indicator];
    }
    else if (mode == MBProgressHUDModeText) {
	// 纯文本
        [indicator removeFromSuperview];
        indicator = nil;
    }
    indicator.translatesAutoresizingMaskIntoConstraints = NO;
    self.indicator = indicator;

    if ([indicator respondsToSelector:@selector(setProgress:)]) {
        [(id)indicator setValue:@(self.progress) forKey:@"progress"];
    }

    [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
    [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];

    [self updateViewsForColor:self.contentColor];
    [self setNeedsUpdateConstraints];
}

五、结束函数

结束函数最终都会走到hideAnimated函数里来,这里有个小细节,会判断显示时间如果小于设定的minShowTime时间的话就会延迟隐藏自己

- (void)hideAnimated:(BOOL)animated {
    MBMainThreadAssert();
    [self.graceTimer invalidate];
    self.useAnimation = animated;
    self.finished = YES;
    // 如果显示时间太短,则延迟隐藏
    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;
        } 
    }
    // 立即隐藏
    [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;
        self.bezelView.alpha = 0.f;
        self.backgroundView.alpha = 1.f;
        [self done];
    }
}

最后的清理工作和回调调用

- (void)done {
    [self.hideDelayTimer invalidate];
    [self setNSProgressDisplayLinkEnabled:NO];

    if (self.hasFinished) {
        self.alpha = 0.0f;
        if (self.removeFromSuperViewOnHide) {
            [self removeFromSuperview];
        }
    }
    MBProgressHUDCompletionBlock completionBlock = self.completionBlock;
    if (completionBlock) {
        completionBlock();
    }
    id<MBProgressHUDDelegate> delegate = self.delegate;
    if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
        [delegate performSelector:@selector(hudWasHidden:) withObject:self];
    }
}

小细节:

1、MBMainThreadAssert(),断言当前是否在主线程执行,宏定义如下

#define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread.");

2、定义常量

extern CGFloat const MBProgressMaxOffset;
CGFloat const MBProgressMaxOffset = 1000000.f;






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值