SVProgressHUD原理解析

概述

SVProgressHUD V2.1.2是一款運(yùn)行于iOS、tvOS中的輕量級(jí)指示器, 常用于指示一個(gè)任務(wù)正在持續(xù)進(jìn)行中, 其采用單例模式創(chuàng)建對(duì)象, 所以我們?cè)谑褂眠^(guò)程中只需通過(guò)[SVProgressHUD method]的方式調(diào)用對(duì)應(yīng)方法即可

[SVProgressHUD show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // time-consuming task
    dispatch_async(dispatch_get_main_queue(), ^{
        [SVProgressHUD dismiss];
    });
});
SVProgressHUD.gif

在網(wǎng)絡(luò)上關(guān)于SVProgressHUD框架的使用教程有很多, 本文對(duì)此不再贅述, 本文將重點(diǎn)介紹框架的結(jié)構(gòu)和技術(shù)點(diǎn)

SVIndefiniteAnimatedView

概述

SVIndefiniteAnimatedView繼承自UIView類, 用于實(shí)現(xiàn)一個(gè)無(wú)限指示器, 該類在.h文件中提供如下3個(gè)屬性分別用于定義無(wú)限指示器的厚度、半徑及顏色

@property (nonatomic, assign) CGFloat strokeThickness;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, strong) UIColor *strokeColor;

實(shí)現(xiàn)原理

無(wú)限指示器原理

SVIndefiniteAnimatedView類在.m文件中提供如下屬性, 利用該屬性便可非常巧妙地實(shí)現(xiàn)無(wú)限指示器效果

@property (nonatomic, strong) CAShapeLayer *indefiniteAnimatedLayer;

注: 本文默認(rèn)讀者了解CALayer的mask屬性

indefiniteAnimatedLayer是一個(gè)圓形layer, 其mask遮罩屬性是一個(gè)以特定圖片為內(nèi)容的layer(后文稱其為"maskLayer"), 二者如下圖所示

無(wú)限指示器1.png

通過(guò)CABasicAnimation針對(duì)maskLayer的transform.rotation添加動(dòng)畫, 使其不斷地順時(shí)針進(jìn)行旋轉(zhuǎn); 通過(guò)CAAnimationGroup為indefiniteAnimatedLayer的strokeStart和strokeEnd添加動(dòng)畫, 使其不斷地順時(shí)針進(jìn)行旋轉(zhuǎn), 同時(shí)保證擁有一個(gè)不變的缺口, 二者如下圖所示

無(wú)限指示器2.gif

將maskLayer作為indefiniteAnimatedLayer的mask遮罩屬性, 便實(shí)現(xiàn)了無(wú)限指示器效果

無(wú)限指示器3.gif
- (CAShapeLayer*)indefiniteAnimatedLayer {
    if(!_indefiniteAnimatedLayer) {
        CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5);
        UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:self.radius startAngle:(CGFloat) (M_PI*3/2) endAngle:(CGFloat) (M_PI/2+M_PI*5) clockwise:YES];
        
        _indefiniteAnimatedLayer = [CAShapeLayer layer];
        _indefiniteAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale];
        _indefiniteAnimatedLayer.frame = CGRectMake(0.0f, 0.0f, arcCenter.x*2, arcCenter.y*2);
        _indefiniteAnimatedLayer.fillColor = [UIColor clearColor].CGColor;
        _indefiniteAnimatedLayer.strokeColor = self.strokeColor.CGColor;
        _indefiniteAnimatedLayer.lineWidth = self.strokeThickness;
        _indefiniteAnimatedLayer.lineCap = kCALineCapRound;
        _indefiniteAnimatedLayer.lineJoin = kCALineJoinBevel;
        _indefiniteAnimatedLayer.path = smoothedPath.CGPath;
        
        CALayer *maskLayer = [CALayer layer];
        
        NSBundle *bundle = [NSBundle bundleForClass:[SVProgressHUD class]];
        NSURL *url = [bundle URLForResource:@"SVProgressHUD" withExtension:@"bundle"];
        NSBundle *imageBundle = [NSBundle bundleWithURL:url];
        
        NSString *path = [imageBundle pathForResource:@"angle-mask" ofType:@"png"];
        
        maskLayer.contents = (__bridge id)[[UIImage imageWithContentsOfFile:path] CGImage];
        maskLayer.frame = _indefiniteAnimatedLayer.bounds;
        _indefiniteAnimatedLayer.mask = maskLayer;
        
        NSTimeInterval animationDuration = 1;
        CAMediaTimingFunction *linearCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        animation.fromValue = (id) 0;
        animation.toValue = @(M_PI*2);
        animation.duration = animationDuration;
        animation.timingFunction = linearCurve;
        animation.removedOnCompletion = NO;
        animation.repeatCount = INFINITY;
        animation.fillMode = kCAFillModeForwards;
        animation.autoreverses = NO;
        [maskLayer addAnimation:animation forKey:@"rotate"];
        
        CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
        animationGroup.duration = animationDuration;
        animationGroup.repeatCount = INFINITY;
        animationGroup.removedOnCompletion = NO;
        animationGroup.timingFunction = linearCurve;
        
        CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
        strokeStartAnimation.fromValue = @0.015;
        strokeStartAnimation.toValue = @0.515;
        
        CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        strokeEndAnimation.fromValue = @0.485;
        strokeEndAnimation.toValue = @0.985;
        
        animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
        [_indefiniteAnimatedLayer addAnimation:animationGroup forKey:@"progress"];
    }
    return _indefiniteAnimatedLayer;
}

注: indefiniteAnimatedLayer的動(dòng)畫效果實(shí)現(xiàn)很巧妙, 為了達(dá)到想要的效果, 將indefiniteAnimatedLayer的path設(shè)置為兩周, 這里讀者可以仔細(xì)體會(huì)

顯示原理

SVIndefiniteAnimatedView類中重寫了如下方法, 當(dāng)父視圖存在時(shí)(即視圖被add時(shí)), 將indefiniteAnimatedLayer添加為self.layer的子layer; 當(dāng)父視圖不存在時(shí)(即視圖被remove時(shí)), 將indefiniteAnimatedLayer從self.layer中移除

- (void)willMoveToSuperview:(UIView*)newSuperview {
    if (newSuperview) {
        [self layoutAnimatedLayer];
    } else {
        [_indefiniteAnimatedLayer removeFromSuperlayer];
        _indefiniteAnimatedLayer = nil;
    }
}

注: 該方法在父視圖將要發(fā)生改變(add/remove)時(shí)會(huì)被系統(tǒng)調(diào)用, 該方法默認(rèn)實(shí)現(xiàn)沒(méi)有進(jìn)行任何操作, 子類可以覆蓋該方法以執(zhí)行一些額外的操作, 當(dāng)視圖被add時(shí), newSuperview為父視圖; 當(dāng)視圖被remove時(shí), newSuperview為nil

大小原理

SVIndefiniteAnimatedView類中重寫了如下方法, 當(dāng)調(diào)用sizeToFit方法時(shí), 系統(tǒng)會(huì)自動(dòng)調(diào)用如下方法, 并設(shè)置自身大小

- (CGSize)sizeThatFits:(CGSize)size {
    return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2);
}

SVProgressAnimatedView

概述

SVProgressAnimatedView繼承自UIView類, 用于實(shí)現(xiàn)一個(gè)進(jìn)度指示器, 該類在.h文件中提供如下4個(gè)屬性分別用于定義進(jìn)度指示器的厚度、半徑、顏色及進(jìn)度

@property (nonatomic, assign) CGFloat strokeThickness;
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, strong) UIColor *strokeColor;
@property (nonatomic, assign) CGFloat strokeEnd;

實(shí)現(xiàn)原理

進(jìn)度指示器原理

SVProgressAnimatedView類在.m文件中提供如下屬性, 利用該屬性便可非常巧妙地實(shí)現(xiàn)進(jìn)度指示器效果

@property (nonatomic, strong) CAShapeLayer *ringAnimatedLayer;

ringAnimatedLayer是一個(gè)圓形layer, 如下圖所示

進(jìn)度指示器1.png

將兩個(gè)顏色不同的SVProgressAnimatedView疊加, 便實(shí)現(xiàn)了進(jìn)度指示器效果

進(jìn)度指示器2.png
- (CAShapeLayer*)ringAnimatedLayer {
    if(!_ringAnimatedLayer) {
        CGPoint arcCenter = CGPointMake(self.radius+self.strokeThickness/2+5, self.radius+self.strokeThickness/2+5);
        UIBezierPath* smoothedPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:self.radius startAngle:(CGFloat)-M_PI_2 endAngle:(CGFloat) (M_PI + M_PI_2) clockwise:YES];
        
        _ringAnimatedLayer = [CAShapeLayer layer];
        _ringAnimatedLayer.contentsScale = [[UIScreen mainScreen] scale];
        _ringAnimatedLayer.frame = CGRectMake(0.0f, 0.0f, arcCenter.x*2, arcCenter.y*2);
        _ringAnimatedLayer.fillColor = [UIColor clearColor].CGColor;
        _ringAnimatedLayer.strokeColor = self.strokeColor.CGColor;
        _ringAnimatedLayer.lineWidth = self.strokeThickness;
        _ringAnimatedLayer.lineCap = kCALineCapRound;
        _ringAnimatedLayer.lineJoin = kCALineJoinBevel;
        _ringAnimatedLayer.path = smoothedPath.CGPath;
    }
    return _ringAnimatedLayer;
}
顯示原理

SVProgressAnimatedView類中重寫了如下方法, 當(dāng)父視圖存在時(shí)(即視圖被add時(shí)), 將ringAnimatedLayer添加為self.layer的子layer; 當(dāng)父視圖不存在時(shí)(即視圖被remove時(shí)), 將ringAnimatedLayer從self.layer中移除

- (void)willMoveToSuperview:(UIView*)newSuperview {
    if (newSuperview) {
        [self layoutAnimatedLayer];
    } else {
        [_ringAnimatedLayer removeFromSuperlayer];
        _ringAnimatedLayer = nil;
    }
}

注: 該方法在父視圖將要發(fā)生改變(add/remove)時(shí)會(huì)被系統(tǒng)調(diào)用, 該方法默認(rèn)實(shí)現(xiàn)沒(méi)有進(jìn)行任何操作, 子類可以覆蓋該方法以執(zhí)行一些額外的操作, 當(dāng)視圖被add時(shí), newSuperview為父視圖; 當(dāng)視圖被remove時(shí), newSuperview為nil

大小原理

SVProgressAnimatedView類中重寫了如下方法, 當(dāng)調(diào)用sizeToFit方法時(shí), 系統(tǒng)會(huì)自動(dòng)調(diào)用如下方法, 并設(shè)置自身大小

- (CGSize)sizeThatFits:(CGSize)size {
    return CGSizeMake((self.radius+self.strokeThickness/2+5)*2, (self.radius+self.strokeThickness/2+5)*2);
}

SVRadialGradientLayer

概述

SVRadialGradientLayer繼承自CALayer類, 用于實(shí)現(xiàn)一個(gè)放射漸變層, 該類在.h文件中提供如下屬性用于定義放射漸變層的放射中心

@property (nonatomic) CGPoint gradientCenter;

實(shí)現(xiàn)原理

- (void)drawInContext:(CGContextRef)context {
    size_t locationsCount = 2;
    CGFloat locations[2] = {0.0f, 1.0f};
    CGFloat colors[8] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.75f};
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, locations, locationsCount);
    CGColorSpaceRelease(colorSpace);

    float radius = MIN(self.bounds.size.width , self.bounds.size.height);
    CGContextDrawRadialGradient (context, gradient, self.gradientCenter, 0, self.gradientCenter, radius, kCGGradientDrawsAfterEndLocation);
    CGGradientRelease(gradient);
}
放射漸變層.png

SVProgressHUD

概述

SVProgressHUD繼承自UIView類, 該類提供了兩類方法供使用者調(diào)用, 其中+setXXX:方法用于設(shè)置HUD的樣式、遮罩、顏色等, +showXXX:方法用于設(shè)置HUD的顯示, 而+dismissXX:方法用于設(shè)置HUD的隱藏

SVProgressHUD的視圖層級(jí)結(jié)構(gòu)如下圖所示

SVProgressHUD視圖層級(jí)結(jié)構(gòu).png

實(shí)現(xiàn)原理

+setXXX:方法

+setXXX:方法實(shí)現(xiàn)很簡(jiǎn)單, 每一個(gè)方法都只是在調(diào)用對(duì)應(yīng)的-setXXX:方法,

+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType {
    [self sharedView].defaultMaskType = maskType;
}

- (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType {
    if (!_isInitializing) _defaultMaskType = maskType;
}

注: 其中isInitializing在initWithFrame:方法開始時(shí)被設(shè)置為YES, 結(jié)束時(shí)被設(shè)置為NO

+showXXX:方法

SVProgressHUD中提供如下幾種用于展示無(wú)限指示器和進(jìn)度指示器的方法, 他們的調(diào)用流程如下圖所示

+ (void)show;
+ (void)showWithStatus:(NSString*)status;
+ (void)showProgress:(float)progress;
+ (void)showProgress:(float)progress status:(NSString*)status;
SVProgressHUD方法調(diào)用流程1.png

通過(guò)觀察我們可以發(fā)現(xiàn), 幾個(gè)方法最終都會(huì)調(diào)用如下方法, 接下來(lái)我們將對(duì)該方法進(jìn)行分析(注: 筆者僅摘錄了方法核心部分)

- (void)showProgress:(float)progress status:(NSString*)status {
    __weak SVProgressHUD *weakSelf = self;
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        if(strongSelf){
            [strongSelf updateViewHierarchy];
            
            strongSelf.imageView.hidden = YES;
            strongSelf.imageView.image = nil;
            strongSelf.fadeOutTimer = nil;
            
            strongSelf.statusLabel.text = status;
            strongSelf.progress = progress;
            
            if(progress >= 0) {
                [strongSelf cancelIndefiniteAnimatedViewAnimation];
                
                if(!strongSelf.ringView.superview){
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
                    [strongSelf.hudVibrancyView.contentView addSubview:strongSelf.ringView];
#else
                    [strongSelf.hudView addSubview:strongSelf.ringView];
#endif
                }
                if(!strongSelf.backgroundRingView.superview){
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
                    [strongSelf.hudVibrancyView.contentView addSubview:strongSelf.backgroundRingView];
#else
                    [strongSelf.hudView addSubview:strongSelf.backgroundRingView];
#endif
                }
                
                [CATransaction begin];
                [CATransaction setDisableActions:YES];
                strongSelf.ringView.strokeEnd = progress;
                [CATransaction commit];
            } else {
                [strongSelf cancelRingLayerAnimation];
                
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
                [strongSelf.hudVibrancyView.contentView addSubview:strongSelf.indefiniteAnimatedView];
#else
                [strongSelf.hudView addSubview:strongSelf.indefiniteAnimatedView];
#endif
                if([strongSelf.indefiniteAnimatedView respondsToSelector:@selector(startAnimating)]) {
                    [(id)strongSelf.indefiniteAnimatedView startAnimating];
                }
            }
            
            [strongSelf showStatus:status];
        }
    }];
}

通過(guò)梳理, 該方法流程如下圖所示

SVProgressHUD方法調(diào)用流程4.png

SVProgressHUD中提供如下幾種用于展示圖片的方法, 他們的調(diào)用流程如下圖所示

+ (void)showInfoWithStatus:(NSString*)status;
+ (void)showSuccessWithStatus:(NSString*)status;
+ (void)showErrorWithStatus:(NSString*)status;
+ (void)showImage:(UIImage*)image status:(NSString*)status;
SVProgressHUD方法調(diào)用流程2.png

通過(guò)觀察我們可以發(fā)現(xiàn), 幾個(gè)方法最終都會(huì)調(diào)用如下方法, 接下來(lái)我們將對(duì)該方法進(jìn)行分析(注: 筆者僅摘錄了方法核心部分)

- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration {
    __weak SVProgressHUD *weakSelf = self;
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        if(strongSelf){
            [strongSelf updateViewHierarchy];
            
            strongSelf.progress = SVProgressHUDUndefinedProgress;
            [strongSelf cancelRingLayerAnimation];
            [strongSelf cancelIndefiniteAnimatedViewAnimation];
            
            UIColor *tintColor = strongSelf.foregroundColorForStyle;
            UIImage *tintedImage = image;
            if (image.renderingMode != UIImageRenderingModeAlwaysTemplate) {
                tintedImage = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
            }
            strongSelf.imageView.tintColor = tintColor;
            strongSelf.imageView.image = tintedImage;
            strongSelf.imageView.hidden = NO;
            
            strongSelf.statusLabel.text = status;
            
            [strongSelf showStatus:status];
            
            strongSelf.fadeOutTimer = [NSTimer timerWithTimeInterval:duration target:strongSelf selector:@selector(dismiss) userInfo:nil repeats:NO];
            [[NSRunLoop mainRunLoop] addTimer:strongSelf.fadeOutTimer forMode:NSRunLoopCommonModes];
        }
    }];
}

通過(guò)梳理, 該方法流程如下圖所示

SVProgressHUD方法調(diào)用流程5.png

上方介紹的兩個(gè)方法都會(huì)調(diào)用如下方法, 接下來(lái)我們將對(duì)該方法進(jìn)行分析(注: 筆者僅摘錄了方法核心部分)

- (void)showStatus:(NSString*)status {
    [self updateHUDFrame];
    [self positionHUD:nil];
    
    if(self.defaultMaskType != SVProgressHUDMaskTypeNone) {
        self.controlView.userInteractionEnabled = YES;
    } else {
        self.controlView.userInteractionEnabled = NO;
    }
    
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if(self.hudView.contentView.alpha != 1.0f){
#else
    if(self.hudView.alpha != 1.0f){
#endif
        [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification
                                                            object:self
                                                          userInfo:[self notificationUserInfo]];
        
        self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3);
        
        __block void (^animationsBlock)(void) = ^{
            self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1/1.3f, 1/1.3f);
            
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
            self.hudView.contentView.alpha = 1.0f;
#else
            self.hudView.alpha = 1.0f;
#endif
            self.backgroundView.alpha = 1.0f;
        };
        
        __block void (^completionBlock)(void) = ^{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
            if(self.hudView.contentView.alpha == 1.0f){
#else
            if(self.hudView.alpha == 1.0f){
#endif
                [self registerNotifications];
                
                [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification
                                                                    object:self
                                                                  userInfo:[self notificationUserInfo]];
            }
        };
        
        if (self.fadeInAnimationDuration > 0) {
            [UIView animateWithDuration:self.fadeInAnimationDuration
                                  delay:0
                                options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseIn | UIViewAnimationOptionBeginFromCurrentState)
                             animations:^{
                                 animationsBlock();
                             } completion:^(BOOL finished) {
                                 completionBlock();
                             }];
        } else {
            animationsBlock();
            completionBlock();
        }
        
        [self setNeedsDisplay];
    }
}

通過(guò)梳理, 該方法流程如下圖所示

SVProgressHUD方法調(diào)用流程6.png
+dismissXXX:方法

SVProgressHUD中提供如下幾種用于隱藏的方法, 他們的調(diào)用流程如下圖所示

+ (void)dismiss;
+ (void)dismissWithCompletion:(SVProgressHUDDismissCompletion)completion;
+ (void)dismissWithDelay:(NSTimeInterval)delay;
+ (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion;
SVProgressHUD方法調(diào)用流程3.png

通過(guò)觀察我們可以發(fā)現(xiàn), 幾個(gè)方法最終都會(huì)調(diào)用如下方法, 接下來(lái)我們將對(duì)該方法進(jìn)行分析(注: 筆者僅摘錄了方法核心部分)

- (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion {
    __weak SVProgressHUD *weakSelf = self;
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        if(strongSelf){
            [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillDisappearNotification
                                                                object:nil
                                                              userInfo:[strongSelf notificationUserInfo]];
            
            __block void (^animationsBlock)(void) = ^{
                strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f);
                
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
                strongSelf.hudView.contentView.alpha = 0.0f;
#else
                strongSelf.hudView.alpha = 0.0f;
#endif
                strongSelf.backgroundView.alpha = 0.0f;
            };
            
            __block void (^completionBlock)(void) = ^{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
                if(strongSelf.hudView.contentView.alpha == 0.0f){
#else
                if(strongSelf.hudView.alpha == 0.0f){
#endif
                    [strongSelf.controlView removeFromSuperview];
                    [strongSelf.backgroundView removeFromSuperview];
                    [strongSelf.hudView removeFromSuperview];
                    [strongSelf removeFromSuperview];
                    
                    strongSelf.progress = SVProgressHUDUndefinedProgress;
                    [strongSelf cancelRingLayerAnimation];
                    [strongSelf cancelIndefiniteAnimatedViewAnimation];
                    
                    [[NSNotificationCenter defaultCenter] removeObserver:strongSelf];
                    
                    [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidDisappearNotification
                                                                        object:strongSelf
                                                                      userInfo:[strongSelf notificationUserInfo]];
                    
#if !defined(SV_APP_EXTENSIONS) && TARGET_OS_IOS
                    UIViewController *rootController = [[UIApplication sharedApplication] keyWindow].rootViewController;
                    [rootController setNeedsStatusBarAppearanceUpdate];
#endif
                    
                    if (completion) {
                        completion();
                    }
                }
            };
            
            dispatch_time_t dipatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
            dispatch_after(dipatchTime, dispatch_get_main_queue(), ^{
                if (strongSelf.fadeOutAnimationDuration > 0) {
                    [UIView animateWithDuration:strongSelf.fadeOutAnimationDuration
                                          delay:0
                                        options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState)
                                     animations:^{
                                         animationsBlock();
                                     } completion:^(BOOL finished) {
                                         completionBlock();
                                     }];
                } else {
                    animationsBlock();
                    completionBlock();
                }
            });
            
            [strongSelf setNeedsDisplay];
        } else if (completion) {
            completion();
        }
    }];
}

通過(guò)梳理, 該方法流程如下圖所示

SVProgressHUD方法調(diào)用流程7.png

結(jié)語(yǔ)

通過(guò)+setXXX:方法設(shè)置的樣式、遮罩、顏色等必須在+showXXX:方法之前調(diào)用方可生效, 但是經(jīng)過(guò)測(cè)試發(fā)現(xiàn), 即使放在+showXXX:方法之后調(diào)用亦可生效(兩個(gè)方法在同一方法中調(diào)用, 而非過(guò)一會(huì)再調(diào)用), 筆者認(rèn)為是因?yàn)樵谌缦氯齻€(gè)方法的實(shí)現(xiàn)中調(diào)用了[[NSOperationQueue mainQueue] addOperationWithBlock:^{}], 這樣便是向主操作隊(duì)列中添加了一個(gè)操作, 而該操作被排在了+setXXX:和+showXXX:的后面, 所以這兩個(gè)方法的調(diào)用順序不影響最終的結(jié)果

- (void)showProgress:(float)progress status:(NSString*)status;
- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration;
- (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion;

SVProgressHUD開源框架從2011年維護(hù)至今, 由75位貢獻(xiàn)者發(fā)布了30個(gè)release版本, 這里集合了眾人的智慧. 筆者在分析代碼時(shí)可能有理解錯(cuò)誤之處, 望讀者不吝指出, 謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載:http://www.itdecent.cn/p/32fcadd12108 每個(gè)UIView有一個(gè)伙伴稱為l...
    F麥子閱讀 6,567評(píng)論 0 13
  • 每個(gè)UIView有一個(gè)伙伴稱為layer,一個(gè)CALayer。UIView實(shí)際上并沒(méi)有把自己畫到屏幕上;它繪制本身...
    shenzhenboy閱讀 3,250評(píng)論 0 17
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,716評(píng)論 25 709
  • 在什么時(shí)候應(yīng)該使用文檔字符串而不是#字注釋?比較大的文檔,模塊,文件,類等,#勇于表達(dá)難以理解的表達(dá)式語(yǔ)句或者微小...
    蘇流云閱讀 164評(píng)論 0 1
  • 今天早早的下了班,回到學(xué)校開組會(huì)。 今天是師弟師妹們的開題報(bào)告預(yù)講,一年前的今天,同樣的辦公室,同樣的老師,我也經(jīng)...
    方塊V閱讀 263評(píng)論 5 2

友情鏈接更多精彩內(nèi)容