MBProgressHUD詳解(二)

MBProgressHUD詳解(二)

外觀

先來一發(fā)圖片,MBProgressHUD整體布局如上圖帜篇,圖片手工畫的比較丑,將就著看吧~~~


1.backgroundView 整個背景圖層,可以通過MBBackgroundView的style屬性設(shè)置樣式。跟系統(tǒng)有關(guān)
2.bezel視圖蜕企,提供indicator、label冠句、detailLabel轻掩、button的背景,用來突出顯示 這個可以通過animationType屬性設(shè)置動畫效果轩端,其實也是可選的,當mode值為MBProgressHUDModeText時逝变,只有文本提示
3.indicator控件基茵,指示器顯示進度情況 這個視圖由我們設(shè)定的mode屬性決定,可以是菊花壳影、進度條拱层,也可以是我們自定義的視圖
4.label控件,顯示簡要提示 (可選)
5.detailLabel控件宴咧,顯示詳細提示 (可選)
6.button按鈕控件根灯,提供中間控制動作,注意:button這個按鈕只有在添加響應(yīng)事件時才顯示 (可選)
style掺栅、mode烙肺、animationType可以看MBProgressHUD.h文件中的枚舉,在<strong>MBProgressHUD詳解(一)</strong>中介紹
</br>

MBProgressHUD對象的繪制

我們通過頭文件可以看到氧卧,MBProgressHUD提供了三個類函數(shù)

//創(chuàng)建一個新的HUD桃笙,并把它顯示在view之上,還可以設(shè)置是否以動畫的形式,此外沙绝,該函數(shù)返回一個HUD的對象
//默認removeFromSuperViewOnHide屬性為YES
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;

//找到最上層的HUD subview 并把它隱藏,成功為YES搏明、其他情況為NO
//同時置removeFromSuperViewOnHide = YES
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated;

//返回最上層的HUD subview
+ (nullable MBProgressHUD *)HUDForView:(UIView *)view;

常用的也就第一個函數(shù)+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;推薦使用鼠锈,這也是github中實例中使用的
</br>
此外也提供了幾個是實例函數(shù)

//以view為基準創(chuàng)建初始化一個HUD對象,為HUD的初始化構(gòu)造函數(shù)
- (instancetype)initWithView:(UIView *)view;

//顯示HUD控件星著,此函數(shù)應(yīng)該在主線程中調(diào)用
- (void)showAnimated:(BOOL)animated;

//隱藏HUD控件购笆,animated控制是否顯示動畫。對應(yīng)于- (void)showAnimated:(BOOL)animated;
- (void)hideAnimated:(BOOL)animated;

//在delay時間之后隱藏HUD虚循,animated控制顯示動畫與否同欠,delay控制延遲時間
- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay;

比較常用的有兩個個函數(shù)- (void)hideAnimated:(BOOL)animated;- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay;

接下來我們就根據(jù)程序的執(zhí)行過程來一步一步分析一下代碼

初始化

+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
    MBProgressHUD *hud = [[self alloc] initWithView:view]; //創(chuàng)建并初始化MBProgressHUD對象
    hud.removeFromSuperViewOnHide = YES; //設(shè)置removeFromSuperViewOnHide屬性
    [view addSubview:hud];
    [hud showAnimated:animated]; //添加到父View中,并顯示
    return hud;   //返回自身
}

這個函數(shù)主要調(diào)用了兩個方法- (id)initWithView:(UIView *)view- (void)showAnimated:(BOOL)animated
函數(shù)- (id)initWithView:(UIView *)view最終調(diào)用- (void)commonInit初始化設(shè)置一些屬性

- (void)commonInit {
    //設(shè)置默認屬性
    // Set default values for properties
    _animationType = MBProgressHUDAnimationFade;
    _mode = MBProgressHUDModeIndeterminate;
    _margin = 20.0f;
    _opacity = 1.f;
    _defaultMotionEffectsEnabled = YES;

    // Default color, depending on the current iOS version
    BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0;
    _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f];
    // Transparent background
    self.opaque = NO;
    self.backgroundColor = [UIColor clearColor];
    // Make it invisible for now
    self.alpha = 0.0f;
    self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    self.layer.allowsGroupOpacity = NO;

    [self setupViews]; //設(shè)置所需的子view 注意此時各個view的位置大小還未確定
    [self updateIndicators];  //設(shè)置指示器樣式
    [self registerForNotifications]; //注冊系統(tǒng)通知
}

這個函數(shù)里面又調(diào)用了三個函數(shù)setupViews邮丰、updateIndicators行您、registerForNotifications,這三個函數(shù)的主要作用上面代碼注釋都說明了剪廉。特別注意的是setupViews函數(shù)返回時娃循,各個view的位置大小還未確定。這里我們主要介紹前面兩個函數(shù)setupViews和updateIndicators斗蒋,上代碼,基本的地方都有注釋

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

    //創(chuàng)建背景視圖
    MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds];
    backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
    backgroundView.backgroundColor = [UIColor clearColor];
    //自動調(diào)整view的寬度捌斧,保證左邊距和右邊距不變 | 自動調(diào)整view的高度,以保證上邊距和下邊距不變
    backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    backgroundView.alpha = 0.f;
    [self addSubview:backgroundView];
    _backgroundView = backgroundView;
    
    //創(chuàng)建小方塊背景視圖
    MBBackgroundView *bezelView = [MBBackgroundView new];
    //代碼層面使用Autolayout,需要對使用的View的translatesAutoresizingMaskIntoConstraints的屬性設(shè)置為NO
    bezelView.translatesAutoresizingMaskIntoConstraints = NO;
    bezelView.layer.cornerRadius = 5.f;
    bezelView.alpha = 0.f;
    [self addSubview:bezelView];
    _bezelView = bezelView;
    [self updateBezelMotionEffects]; //設(shè)置視差效果

    //創(chuàng)建label信息標簽泉沾,提示簡要信息
    UILabel *label = [UILabel new];
    //取消文字大小自適應(yīng)
    label.adjustsFontSizeToFitWidth = NO;
    label.textAlignment = NSTextAlignmentCenter;
    label.textColor = defaultColor;
    label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize];
    //告訴系統(tǒng)渲染器view是否不透明捞蚂,設(shè)置YES可以加快渲染,默認為YES跷究,如果設(shè)置了alpha值姓迅,應(yīng)該設(shè)置為NO
    label.opaque = NO;
    label.backgroundColor = [UIColor clearColor];
    _label = label;

    //創(chuàng)建detailsLabel信息標簽,提示詳細信息
    UILabel *detailsLabel = [UILabel new];
    //取消文字大小自適應(yīng)
    detailsLabel.adjustsFontSizeToFitWidth = NO;
    detailsLabel.textAlignment = NSTextAlignmentCenter;
    detailsLabel.textColor = defaultColor;
    detailsLabel.numberOfLines = 0;
    detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
    //告訴系統(tǒng)渲染器view是否不透明俊马,設(shè)置YES可以加快渲染丁存,默認為YES,如果設(shè)置了alpha值柴我,應(yīng)該設(shè)置為NO
    detailsLabel.opaque = NO;
    detailsLabel.backgroundColor = [UIColor clearColor];
    _detailsLabel = detailsLabel;

    //創(chuàng)建事件響應(yīng)按鈕
    UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom];
    button.titleLabel.textAlignment = NSTextAlignmentCenter;
    button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
    [button setTitleColor:defaultColor forState:UIControlStateNormal];
    _button = button;
    
    //將label detailLabel button添加到蒙版視圖
    for (UIView *view in @[label, detailsLabel, button]) {
        //View的translatesAutoresizingMaskIntoConstraints的屬性設(shè)置為NO,以使用Autolayout
        view.translatesAutoresizingMaskIntoConstraints = NO;
        //當試圖變化時解寝,設(shè)置水平和垂直方向變化的優(yōu)先權(quán)
        //這是設(shè)置每一個view的優(yōu)先權(quán)都是998,對自動布局不熟艘儒。聋伦。不知有何用。界睁。尷尬
        [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;
}

由代碼我們可以看出觉增,這個函數(shù)首先創(chuàng)建了backgroundView、bezelView翻斟、label抑片、detailsLabel、button杨赤,中間使用了一個for循環(huán)把label敞斋、detailsLabel截汪、button添加到bezelView視圖中,最后還創(chuàng)建了頂部視圖和底部視圖植捎,不過默認是隱藏的衙解。
最初看這里的時候有個小疑惑,這里明明創(chuàng)建了button但是如果沒有設(shè)置button屬性焰枢,這個按鈕是不會顯示的蚓峦。原來這里重新寫了一個UIbutton的子類MBProgressHUDRoundedButton,這個子類重寫了一個函數(shù)- (CGSize)intrinsicContentSize,這個函數(shù)也就是控件的內(nèi)置大小济锄。比如UILabel暑椰,UIButton等控件,他們都有自己的內(nèi)置大小荐绝。我們可以重寫這個函數(shù)設(shè)置控件的大小一汽。。

- (CGSize)intrinsicContentSize {
    // Only show, if we have associated control events.
    //allContorlEvents 獲取所有的事件集合
    //只有當有事件才顯示
    if (self.allControlEvents == 0) return CGSizeZero;
    CGSize size = [super intrinsicContentSize];
    // Add some side padding.
    size.width += 20.f;
    return size;
}

我們可以看到低滩,如果這個button沒有任何事件的話召夹,它的大小就是CGSizeZero(沒有大小)恕沫。
接下來我們看一下另一個函數(shù)

- (void)updateIndicators {
    UIView *indicator = self.indicator;
    //判斷目前的指示器是否為UIActivityIndicatorView
    BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]];
    //判斷目前的指示器是否為UIActivityIndicatorView
    BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]];

    MBProgressHUDMode mode = self.mode;
    if (mode == MBProgressHUDModeIndeterminate) { //系統(tǒng)自帶的指示器
        if (!isActivityIndicator) { //如果目前指示器不是UIActivityIndicatorView监憎,則移除之前的indicator創(chuàng)建新的
            // Update to indeterminate indicator
            [indicator removeFromSuperview];
            indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
            [(UIActivityIndicatorView *)indicator startAnimating];
            [self.bezelView addSubview:indicator];
        }
    }
    else if (mode == MBProgressHUDModeDeterminateHorizontalBar) { //棒狀指示器進度條
        // Update to bar determinate indicator
        [indicator removeFromSuperview];
        indicator = [[MBBarProgressView alloc] init];
        [self.bezelView addSubview:indicator];
    }
    else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) { //圓形指示器 默認為圓餅
        if (!isRoundIndicator) {
            // Update to determinante indicator
            [indicator removeFromSuperview];
            indicator = [[MBRoundProgressView alloc] init];
            [self.bezelView addSubview:indicator];
        }
        if (mode == MBProgressHUDModeAnnularDeterminate) { //圓環(huán)指示器
            [(MBRoundProgressView *)indicator setAnnular:YES];
        }
    } 
    else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) { //自定義視圖指示器
        // Update custom view indicator
        [indicator removeFromSuperview];
        indicator = self.customView;
        [self.bezelView addSubview:indicator];
    }
    else if (mode == MBProgressHUDModeText) { //文本形式,去除指示器視圖
        [indicator removeFromSuperview];
        indicator = nil;
    }
    //View的translatesAutoresizingMaskIntoConstraints的屬性設(shè)置為NO,以使用Autolayout
    indicator.translatesAutoresizingMaskIntoConstraints = NO;
    self.indicator = indicator;

    if ([indicator respondsToSelector:@selector(setProgress:)]) {
        [(id)indicator setValue:@(self.progress) forKey:@"progress"]; //設(shè)置進度條的數(shù)值
    }
    
    //當試圖變化時婶溯,設(shè)置水平和垂直方向變化的優(yōu)先權(quán)
    [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
    [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];

    [self updateViewsForColor:self.contentColor]; //設(shè)置控件顏色的顏色
    [self setNeedsUpdateConstraints]; //更新布局,系統(tǒng)自動調(diào)用updateConstraints
}

這個函數(shù)主要是用來設(shè)置indicator指示器鲸阔,根據(jù)mode的屬性顯示不同的形式,具體的可以參看代碼注釋迄委,系統(tǒng)提供的菊花形狀的指示器我們就不過多說明了褐筛,一會我們著重介紹一下MBProgressHUDModeDeterminateHorizontalBar模式。
再次回到這個函數(shù)中跑筝,這個函數(shù)最后調(diào)用的是setNeedsUpdateConstraints函數(shù)死讹,這個函數(shù)會自動調(diào)用updateConstraints瞒滴,這個函數(shù)主要作用是更新一下各個控件的布局曲梗,代碼如下:

//系統(tǒng)自動調(diào)用
- (void)updateConstraints {
    UIView *bezel = self.bezelView;
    UIView *topSpacer = self.topSpacer;
    UIView *bottomSpacer = self.bottomSpacer;
    CGFloat margin = self.margin;
    NSMutableArray *bezelConstraints = [NSMutableArray array];
    NSDictionary *metrics = @{@"margin": @(margin)};

    NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil];
    //insertObject:atIndex是插入到指定 索引的前面,即插入到數(shù)組subviews中self.label元素的前面
    if (self.indicator) [subviews insertObject:self.indicator atIndex:1];

    // Remove existing constraintes
    //移除所有約束
    [self removeConstraints:self.constraints];
    [topSpacer removeConstraints:topSpacer.constraints];
    [bottomSpacer removeConstraints:bottomSpacer.constraints];
    if (self.bezelConstraints) {
        [bezel removeConstraints:self.bezelConstraints];
        self.bezelConstraints = nil;
    }

    // Center bezel in container (self), applying the offset if set
    //將bezel View居中顯示,如果設(shè)置了偏移offset妓忍,則同時設(shè)置偏移
    CGPoint offset = self.offset;
    NSMutableArray *centeringConstraints = [NSMutableArray array];
    [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]];
    [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]];
    [self applyPriority:998.f toConstraints:centeringConstraints];
    [self addConstraints:centeringConstraints];

    // Ensure minimum side margin is kept
    //與邊界保持最小間隔
    NSMutableArray *sideConstraints = [NSMutableArray array];
    [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
    [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]];
    [self applyPriority:999.f toConstraints:sideConstraints];
    [self addConstraints:sideConstraints];

    // Minimum bezel size, if set
    //如果定義了最小的寬高虏两,這設(shè)置其最小大小
    CGSize minimumSize = self.minSize;
    if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) {
        NSMutableArray *minSizeConstraints = [NSMutableArray array];
        [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]];
        [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]];
        [self applyPriority:997.f toConstraints:minSizeConstraints];
        [bezelConstraints addObjectsFromArray:minSizeConstraints];
    }

    // Square aspect ratio, if set
    //強制寬高相等
    if (self.square) {
        NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0];
        square.priority = 997.f;
        [bezelConstraints addObject:square];
    }

    //top和bottom設(shè)置
    // Top and bottom spacing
    [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
    [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]];
    // Top and bottom spaces should be equal
    [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]];

    // Layout subviews in bezel
    //bezel里面的子視圖大小設(shè)置
    NSMutableArray *paddingConstraints = [NSMutableArray new];
    [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) {
        // Center in bezel
        [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]];
        // Ensure the minimum edge margin is kept
        [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]];
        // Element spacing
        if (idx == 0) {
            // First, ensure spacing to bezel edge
            [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]];
        } else if (idx == subviews.count - 1) {
            // Last, ensure spacigin to bezel edge
            [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]];
        }
        if (idx > 0) {
            // Has previous
            NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f];
            [bezelConstraints addObject:padding];
            [paddingConstraints addObject:padding];
        }
    }];

    [bezel addConstraints:bezelConstraints];
    self.bezelConstraints = bezelConstraints;
    
    self.paddingConstraints = [paddingConstraints copy];
    [self updatePaddingConstraints];
    
    [super updateConstraints];
}

這里面用到了自動布局AutoLayout的技術(shù),如果需要深入了解的可以自行查閱文檔世剖。定罢。
至此,PUD對象的創(chuàng)建工作就完成旁瘫,現(xiàn)在我們來看一下指示器的幾種形式祖凫,通過代碼可知琼蚯,PUD提供了幾種指示器的形式菊花、棒狀進度條惠况,圓餅/圓環(huán)進度條遭庶。在這里我們著重介紹一下棒狀進度條。

棒狀進度條是MBBarProgressView這個類實現(xiàn)的稠屠,通過- (void)drawRect:(CGRect)rect這個函數(shù)繪制峦睡。

//設(shè)置棒狀進度條背景
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetLineWidth(context, 2);
    CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]);
    //CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]);
    CGContextSetFillColorWithColor(context, [[UIColor redColor] CGColor]);
    
    // Draw background
    //季度條背景
    CGFloat radius = (rect.size.height / 2) - 2;
    CGContextMoveToPoint(context, 2, rect.size.height/2);
    CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
    CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
    CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
    CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
    CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
    CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
    CGContextFillPath(context);
    
    // Draw border
    // 進度度條移動中心線
    CGContextMoveToPoint(context, 2, rect.size.height/2);
    CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius);
    CGContextAddLineToPoint(context, rect.size.width - radius - 2, 2);
    CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius);
    CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius);
    CGContextAddLineToPoint(context, radius + 2, rect.size.height - 2);
    CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius);
    CGContextStrokePath(context);
    
    CGContextSetFillColorWithColor(context, [_progressColor CGColor]);
    radius = radius - 2;
    CGFloat amount = self.progress * rect.size.width;
    
    // Progress in the middle area
    // 設(shè)置進度條根據(jù)progress移動變長效果
    if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) {
        CGContextMoveToPoint(context, 4, rect.size.height/2);
        CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
        CGContextAddLineToPoint(context, amount, 4);
        CGContextAddLineToPoint(context, amount, radius + 4);
        
        CGContextMoveToPoint(context, 4, rect.size.height/2);
        CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
        CGContextAddLineToPoint(context, amount, rect.size.height - 4);
        CGContextAddLineToPoint(context, amount, radius + 4);
        
        CGContextFillPath(context);
    }
    
    // Progress in the right arc
    //右邊界圓角效果
    else if (amount > radius + 4) {
        CGFloat x = amount - (rect.size.width - radius - 4);

        CGContextMoveToPoint(context, 4, rect.size.height/2);
        CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
        CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4);
        CGFloat angle = -acos(x/radius);
        if (isnan(angle)) angle = 0;
        CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0);
        CGContextAddLineToPoint(context, amount, rect.size.height/2);

        CGContextMoveToPoint(context, 4, rect.size.height/2);
        CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
        CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4);
        angle = acos(x/radius);
        if (isnan(angle)) angle = 0;
        CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1);
        CGContextAddLineToPoint(context, amount, rect.size.height/2);
        
        CGContextFillPath(context);
    }
    
    // Progress is in the left arc
    // 左邊界圓角效果
    else if (amount < radius + 4 && amount > 0) {
        CGContextMoveToPoint(context, 4, rect.size.height/2);
        CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius);
        CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);

        CGContextMoveToPoint(context, 4, rect.size.height/2);
        CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius);
        CGContextAddLineToPoint(context, radius + 4, rect.size.height/2);
        
        CGContextFillPath(context);
    }
}

這個函數(shù)雖然很長,但是它主要繪制了兩個部分权埠,進度條最外面的橢圓環(huán)和內(nèi)部的進度條榨了,內(nèi)部的進度條根據(jù)其progress實現(xiàn)長短變化。
</br>

顯示

PUD對象的顯示只有一個函數(shù)- (void)showAnimated:(BOOL)animated,代碼如下:

//根據(jù)參數(shù)顯示HUD對象
- (void)showAnimated:(BOOL)animated {
    MBMainThreadAssert(); //顯示放在主線程中
    [self.minShowTimer invalidate]; //取消定時器
    self.useAnimation = animated;
    self.finished = NO;
    // If the grace time is set postpone the HUD display
    //如果設(shè)置了寬限時間graceTime攘蔽,則延遲顯示龙屉,否則直接顯示
    if (self.graceTime > 0.0) {
        //創(chuàng)建定時器,并把它加入到NDRunLoop中
        NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        self.graceTimer = timer;
    } 
    // ... otherwise show the HUD imediately 
    else {
        [self showUsingAnimation:self.useAnimation];
    }
}

這個函數(shù)有個需要注意的地方:此函數(shù)必須在主線程中執(zhí)行秩彤。

消失

PUD對象提供了兩個隱藏的函數(shù)- (void)hide:(BOOL)animated- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay,通過名字就額可以看出第二個函數(shù)是延遲delay時間在隱藏消失叔扼,

- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay {
    //創(chuàng)建定時器,并把它加入到NDRunLoop中
    NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    self.hideDelayTimer = timer;
}

只是簡單的創(chuàng)建一個定時器漫雷,并把定時器加入到NDRunLoop中瓜富,延遲delay執(zhí)行handleHideTimer:函數(shù)。</br>
這兩個函數(shù)最后都調(diào)用函數(shù)- (void)hideAnimated:(BOOL)animated上降盹,代碼如下:

- (void)hideAnimated:(BOOL)animated {
    MBMainThreadAssert();
    [self.graceTimer invalidate]; //時間重置
    self.useAnimation = animated;
    self.finished = YES;
    // If the minShow time is set, calculate how long the hud was shown,
    // and pospone the hiding operation if necessary
    //如果設(shè)置了最小顯示時間与柑,則執(zhí)行此步驟,否則直接隱藏
    if (self.minShowTime > 0.0 && self.showStarted) {
        NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
        if (interv < self.minShowTime) {
            //創(chuàng)建定時器蓄坏,并把它加入到NDRunLoop中
            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];
}

這個函數(shù)同樣設(shè)置了一個定時器价捧,根據(jù)minShowTime屬性,控制PUD顯示的時機涡戳。
</br>
最后的最后结蟋,顯示和隱藏都統(tǒng)一到一個函數(shù)中- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion,在這個函數(shù)中,我們可以設(shè)置一些PUD對象出現(xiàn)和隱藏時的動畫效果渔彰,具體請看代碼注釋嵌屎。

//消失或出現(xiàn)時的伸縮效果,以及透明度
- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
    // Automatically determine the correct
    if (type == MBProgressHUDAnimationZoom) {
        type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut;
    }

    CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f);
    CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f);

    // Set starting state
    UIView *bezelView = self.bezelView;
    if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) {
        bezelView.transform = small;
    } else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) {
        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];
}

刪除

經(jīng)過以上步驟恍涂,PUD經(jīng)歷了創(chuàng)建宝惰、顯示和隱藏,但是對象并沒消失再沧,只是隱藏了尼夺,變透明了。所以還需一個函數(shù)處理一下后續(xù)動作- (void)doneFinished:(BOOL)finished

//完成后清理動作
- (void)doneFinished:(BOOL)finished {
    // Cancel any scheduled hideDelayed: calls
    [self.hideDelayTimer invalidate];

    if (finished) {
        self.alpha = 0.0f;
        if (self.removeFromSuperViewOnHide) {
            //從父視圖中移除自己以及子視圖
            [self removeFromSuperview];
        }
    }

    if (self.completionBlock) {
        MBProgressHUDCompletionBlock block = self.completionBlock;
        self.completionBlock = NULL;
        block();
    }
    id<MBProgressHUDDelegate> delegate = self.delegate;
    if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
        [delegate performSelector:@selector(hudWasHidden:) withObject:self];
    }
}

這個函數(shù)如果removeFromSuperViewOnHide屬性為YES,則將自己從父視圖上移除淤堵,如果有completionBlock回調(diào)函數(shù)寝衫,則執(zhí)行回調(diào),如果實現(xiàn)了代理并實現(xiàn)了代理方法拐邪,則執(zhí)行代理方法竞端。

到這里整個的執(zhí)行流程差不多就算結(jié)束了,剩下的清理工作都是系統(tǒng)自動調(diào)用庙睡,就不過多說明了事富。。

寫的比較亂乘陪,謝謝你們能夠忍著看完统台,如果有什么錯誤或者不恰當?shù)牡胤剑埐涣羟槊娴闹赋鰜矸纫兀餐涣骷餐M步。谤逼。贵扰。

謝謝!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末流部,一起剝皮案震驚了整個濱河市戚绕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枝冀,老刑警劉巖舞丛,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異果漾,居然都是意外死亡球切,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門绒障,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吨凑,“玉大人,你說我怎么就攤上這事户辱⊥叶郏” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵焕妙,是天一觀的道長蒋伦。 經(jīng)常有香客問我弓摘,道長焚鹊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮末患,結(jié)果婚禮上研叫,老公的妹妹穿的比我還像新娘。我一直安慰自己璧针,他們只是感情好嚷炉,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著探橱,像睡著了一般申屹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上隧膏,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天哗讥,我揣著相機與錄音,去河邊找鬼胞枕。 笑死杆煞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的腐泻。 我是一名探鬼主播决乎,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼派桩!你這毒婦竟也來了构诚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤铆惑,失蹤者是張志新(化名)和其女友劉穎唤反,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸭津,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡彤侍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逆趋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盏阶。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖闻书,靈堂內(nèi)的尸體忽然破棺而出名斟,到底是詐尸還是另有隱情,我是刑警寧澤魄眉,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布砰盐,位于F島的核電站,受9級特大地震影響坑律,放射性物質(zhì)發(fā)生泄漏岩梳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦夹厌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抵栈。三九已至告材,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間古劲,已是汗流浹背创葡。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留绢慢,地道東北人灿渴。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像胰舆,于是被迫代替她去往敵國和親骚露。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容