SVProgressHUD(2.0.3)原來(lái)是這樣

有段時(shí)間沒(méi)有寫(xiě)了鹰贵。這個(gè)周末抽空簡(jiǎn)單整理了一下關(guān)于自己對(duì)SVProgressHUD一些看法以及感悟粮坞。過(guò)程中自己感受到堅(jiān)持做開(kāi)源和堅(jiān)持寫(xiě)原創(chuàng)文章的不易蝗岖。時(shí)間是每一個(gè)程序員最寶貴的資源晦雨。

簡(jiǎn)介

SVProgressHUD在iOS開(kāi)發(fā)中用作提示的場(chǎng)景還是非常多的架曹。這里主要從整個(gè)項(xiàng)目的使用及源碼方面就行分析以及附上相關(guān)效果圖。希望能起到拋磚引玉的作用闹瞧。

使用

SVProgrossHUD是通過(guò)單例的方式來(lái)使用绑雄,這種方式也是許多第三方所使用的。也就是快速創(chuàng)建奥邮,不需要手動(dòng)的alloc進(jìn)行實(shí)例化万牺。

  • 使用的場(chǎng)景: 比較合理的場(chǎng)景是在推薦用戶操作之前確定需要執(zhí)行任務(wù)其他任務(wù)的時(shí)候,而不是在刷新洽腺,無(wú)限的滑動(dòng)或者發(fā)送消息等場(chǎng)景脚粟。

常見(jiàn)的使用方式如下:

[SVProgressHUD show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 處理耗時(shí)的操作
    dispatch_async(dispatch_get_main_queue(), ^{
        [SVProgressHUD dismiss];
    });
});

使用+ (void)show; + (void)showWithStatus:(NSString*)string;來(lái)顯示狀態(tài)不明確的操作,用+ (void)showProgress:(CGFloat)progress;+ (void)showProgress:(CGFloat)progress status:(NSString*)status;來(lái)顯示狀態(tài)明確的操作蘸朋,顯示當(dāng)前操作的進(jìn)度核无。


取消+ (void)dismiss;+ (void)dismissWithDelay:(NSTimeInterval)delay;這里順便提一下dismissWithDelay這個(gè)方法之前沒(méi)注意∨号鳎可以延遲取消厕宗,這樣就不用手動(dòng)用GCD的延遲去dismiss了。


如果想平衡調(diào)用的次數(shù)堕担,可以使用+ (void)popActivity; 一旦匹配了調(diào)用show的次數(shù)則會(huì)消失已慢。如果沒(méi)有匹配爭(zhēng)取則不會(huì)消失。其源碼為

+ (void)popActivity {
if([self sharedView].activityCount > 0) {
    [self sharedView].activityCount--;
}
if([self sharedView].activityCount == 0) {
    [[self sharedView] dismiss];
}

}

或者根據(jù)字符串的長(zhǎng)度來(lái)自動(dòng)確定顯示的時(shí)間霹购。當(dāng)調(diào)用下面的這些方法的時(shí)候會(huì)用這種方式

  • (void)showInfoWithStatus:(NSString*)string;
  • (void)showSuccessWithStatus:(NSString*)string;
  • (void)showErrorWithStatus:(NSString*)string;
  • (void)showImage:(UIImage)image status:(NSString)string;

我們可以自定義里面的一些屬性佑惠,比如字體大小,提示圖片等齐疙∧た可以自定的方法如下:

+ (void)setDefaultStyle:(SVProgressHUDStyle)style;                  // default is SVProgressHUDStyleLight
  • (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType; // default is SVProgressHUDMaskTypeNone
  • (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type; // default is SVProgressHUDAnimationTypeFlat
  • (void)setMinimumSize:(CGSize)minimumSize; // default is CGSizeZero, can be used to avoid resizing for a larger message
  • (void)setRingThickness:(CGFloat)width; // default is 2 pt
  • (void)setRingRadius:(CGFloat)radius; // default is 18 pt
  • (void)setRingNoTextRadius:(CGFloat)radius; // default is 24 pt
  • (void)setCornerRadius:(CGFloat)cornerRadius; // default is 14 pt
  • (void)setFont:(UIFont*)font; // default is [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]
  • (void)setForegroundColor:(UIColor*)color; // default is [UIColor blackColor], only used for SVProgressHUDStyleCustom
  • (void)setBackgroundColor:(UIColor*)color; // default is [UIColor whiteColor], only used for SVProgressHUDStyleCustom
  • (void)setBackgroundLayerColor:(UIColor*)color; // default is [UIColor colorWithWhite:0 alpha:0.4], only used for SVProgressHUDMaskTypeCustom
  • (void)setInfoImage:(UIImage*)image; // default is the bundled info image provided by Freepik
  • (void)setSuccessImage:(UIImage*)image; // default is bundled success image from Freepik
  • (void)setErrorImage:(UIImage*)image; // default is bundled error image from Freepik
  • (void)setViewForExtension:(UIView*)view; // default is nil, only used if #define SV_APP_EXTENSIONS is set
  • (void)setMinimumDismissTimeInterval:(NSTimeInterval)interval; // default is 5.0 seconds
  • (void)setFadeInAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds
  • (void)setFadeOutAnimationDuration:(NSTimeInterval)duration; // default is 0.15 seconds

SVProgressHUD默認(rèn)提供兩種樣式SVProgressHUDStyleLight, SVProgressHUDStyleDark,一個(gè)是白色主題,一個(gè)是黑色主題如果想自定義一些顏色可以通過(guò)setForegroundColor and setBackgroundColor不要忘記設(shè)置默認(rèn)樣式 SVProgressHUDStyleCustom


通知贞奋,SVProgressHUD會(huì)使用到四個(gè)通知

SVProgressHUDWillAppearNotification 

SVProgressHUDDidAppearNotification
SVProgressHUDWillDisappearNotification
SVProgressHUDDidDisappearNotification

每一通知會(huì)傳遞一個(gè)userinfo字典傳遞HUD的提示信息赌厅,key為``SVProgressHUDStatusUserInfoKey``。當(dāng)用戶觸摸提示的整個(gè)屏幕的時(shí)候會(huì)發(fā)出``SVProgressHUDDidReceiveTouchEventNotification``通知轿塔,當(dāng)用戶直接觸摸HUD的時(shí)候會(huì)發(fā)出``SVProgressHUDDidTouchDownInsideNotification``通知特愿。

關(guān)鍵類

SVProgroessHUD一共有四個(gè)重要的類仲墨。它們分別是

  • SVPIndefiniteAnimatedView:無(wú)限旋轉(zhuǎn)視圖組件。如下圖:
  • SVProgressAnimatedView:進(jìn)度視圖組件.如下圖


  • SVProgressHUD: 視圖顯示控制類(我們通過(guò)SVProgressHUD這個(gè)類來(lái)使用上兩種視圖組件)揍障。類似于一個(gè)管理類目养。

  • SVRadialGradientLayer:漸變層,當(dāng)我們?cè)O(shè)置遮罩樣式為SVProgressHUDMaskTypeGradient毒嫡,就需要用到這個(gè)層癌蚁。模仿系統(tǒng)UIAlterView的背景效果。
  • SVProgressHUD.bundle: 這里面放的是一些圖片資源文件

關(guān)鍵類分析

SVPIndefiniteAnimatedView

關(guān)于這個(gè)類兜畸,主要是需要講的就是一個(gè)如果實(shí)現(xiàn)無(wú)限加載的動(dòng)畫(huà)效果努释。如上圖的上圖一樣。原理其實(shí)不難咬摇,我這個(gè)給出一個(gè)圖伐蒂,大家應(yīng)該就明白了。

  • 原理也就是不斷地旋轉(zhuǎn)一張具有漸變顏色的圖片菲嘴,然后通過(guò)使用mask來(lái)遮住不需要的部分(結(jié)合layer使用)饿自。

講到這里就不得不提到iOS動(dòng)畫(huà)中的CALayer以及Mask。常見(jiàn)的場(chǎng)景就是CAShapeLayer和mask結(jié)合使用龄坪。

/* A layer whose alpha channel is used as a mask to select between the
 * layer's background and the result of compositing the layer's
 * contents with its filtered background. Defaults to nil. When used as
 * a mask the layer's `compositingFilter' and `backgroundFilters'
 * properties are ignored. When setting the mask to a new layer, the
 * new layer must have a nil superlayer, otherwise the behavior is
 * undefined. Nested masks (mask layers with their own masks) are
 * unsupported. */

@property(nullable, strong) CALayer *mask;

以上是CALayer的頭文件關(guān)于mask的說(shuō)明昭雌,mask實(shí)際上layer內(nèi)容的一個(gè)遮罩。
如果我們把mask是透明的健田,實(shí)際看到的layer是完全透明的烛卧,也就是說(shuō)只有mask的內(nèi)容不透明的部分和layer的疊加部分才會(huì)顯示。如下圖:


有許多很炫酷的動(dòng)畫(huà)效果都是通過(guò)這樣實(shí)現(xiàn)的妓局。比如以下幾種


其中還有Twitter的啟動(dòng)效果

  • 代碼片段
        // 初始化总放,設(shè)置參數(shù)
        _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;

        // 初始化mask,從資源庫(kù)中讀取圖片
        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"];

        // 大部分用法都是類似的,通過(guò)圖片來(lái)作為maskLayer的contents
        maskLayer.contents = (__bridge id)[[UIImage imageWithContentsOfFile:path] CGImage];
        maskLayer.frame = _indefiniteAnimatedLayer.bounds;
        _indefiniteAnimatedLayer.mask = maskLayer;

開(kāi)始做動(dòng)畫(huà)好爬,做動(dòng)畫(huà)分為了兩個(gè)部分局雄,一個(gè)是圖片旋轉(zhuǎn),一個(gè)是動(dòng)畫(huà)組

  • 旋轉(zhuǎn)動(dòng)畫(huà)
        // 設(shè)置動(dòng)畫(huà)的延遲及類型
        NSTimeInterval animationDuration = 1;
        CAMediaTimingFunction *linearCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
        // 注意value類型為id類型
        animation.fromValue = (id) 0;
        animation.toValue = @(M_PI*2);
        animation.duration = animationDuration;
        animation.timingFunction = linearCurve;
        // 這個(gè)參數(shù)不要忘了存炮,是在昨晚動(dòng)畫(huà)之后保持動(dòng)畫(huà)完成的狀態(tài)
        animation.removedOnCompletion = NO;
        animation.repeatCount = INFINITY;
        animation.fillMode = kCAFillModeForwards;
        animation.autoreverses = NO;
        // 將動(dòng)畫(huà)加到mask上
        [_indefiniteAnimatedLayer.mask addAnimation:animation forKey:@"rotate"];

通過(guò)旋轉(zhuǎn)動(dòng)畫(huà)我們看到的就是


然后來(lái)看看動(dòng)畫(huà)組

        // 創(chuàng)建動(dòng)畫(huà)組炬搭,并設(shè)置相關(guān)屬性
        CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
        animationGroup.duration = animationDuration;
        animationGroup.repeatCount = INFINITY;
        animationGroup.removedOnCompletion = NO;
        animationGroup.timingFunction = linearCurve;

        // strokeStart動(dòng)畫(huà)
        CABasicAnimation *strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
        strokeStartAnimation.fromValue = @0.015;
        strokeStartAnimation.toValue = @0.515;

        // strokeEnd動(dòng)畫(huà)
        CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        strokeEndAnimation.fromValue = @0.485;
        strokeEndAnimation.toValue = @0.985;

        // 將動(dòng)畫(huà)加到動(dòng)畫(huà)組
        animationGroup.animations = @[strokeStartAnimation, strokeEndAnimation];
        [_indefiniteAnimatedLayer addAnimation:animationGroup forKey:@"progress"];
       

動(dòng)畫(huà)組的效果

讓我們來(lái)找找數(shù)字之間的關(guān)系

strokeStartAnimation.fromValue = @0.015;
strokeStartAnimation.toValue = @0.515;

strokeEndAnimation.fromValue = @0.485;
strokeEndAnimation.toValue = @0.985;

有些規(guī)律吧。這樣就能達(dá)到不斷改變strokeStart和strokeEnd的值并讓之間的差值為一個(gè)常量穆桂。我們看到的就是一個(gè)白色的缺口不斷旋轉(zhuǎn)的效果宫盔。個(gè)人覺(jué)得實(shí)現(xiàn)這種效果是想到巧妙的

改變strokeEnd參數(shù)的效果

strokeEndAnimation.fromValue = @0.285;
strokeEndAnimation.toValue = @0.785;
  • 其他

其他值得學(xué)習(xí)的大概應(yīng)該算用到了懶加載的方式吧。便于代碼的管理以及縷清邏輯關(guān)系享完。重寫(xiě)屬性的setter方法灼芭,在setter方法里面完成和這個(gè)屬性相關(guān)一些賦值,邏輯判斷操作般又。比如:

- (void)setRadius:(CGFloat)radius {
    if(radius != _radius) {
        _radius = radius;
        
        // 在setter方法中進(jìn)行相關(guān)邏輯判斷彼绷,
        [_indefiniteAnimatedLayer removeFromSuperlayer];
        _indefiniteAnimatedLayer = nil;
        
        if(self.superview) {
            [self layoutAnimatedLayer];
        }
    }
}

SVProgressAnimatedView

這個(gè)是用于處理進(jìn)度的視圖組件,實(shí)現(xiàn)進(jìn)度的原理也很簡(jiǎn)單巍佑,也就是不斷改變strokeEnd的值。
來(lái)看看那.h文件

@interface SVProgressAnimatedView : UIView

// 半徑
@property (nonatomic, assign) CGFloat radius;
// 厚度
@property (nonatomic, assign) CGFloat strokeThickness;
// 進(jìn)度指示顏色
@property (nonatomic, strong) UIColor *strokeColor;

// 當(dāng)前進(jìn)度苛预,
@property (nonatomic, assign) CGFloat strokeEnd;

@end

.m文件的實(shí)現(xiàn)大致和SVIndefiniteAnimatedView一樣笋熬。使用懶加載昔馋,在willMoveToSuperview方法中添加layer秘遏。實(shí)現(xiàn)進(jìn)度的關(guān)鍵就是重寫(xiě)strokeEnd的setter方法

- (void)setStrokeEnd:(CGFloat)strokeEnd {
    _strokeEnd = strokeEnd;
    // 改變結(jié)束的位置
    _ringAnimatedLayer.strokeEnd = _strokeEnd;
}

進(jìn)度寫(xiě)死的效果0.4

順便提一下舍扰,storkeStart使用的默認(rèn)值是0边苹。所以是從正上方開(kāi)始的慕购。

SVProgressHUD

這個(gè)類的作用想到于管理類的作用沪悲,負(fù)責(zé)和外部交互和調(diào)用視圖組件。進(jìn)行重要邏輯判斷握截。

.h文件

extern相關(guān)

來(lái)看看.h文件中extern的使用

extern NSString * const SVProgressHUDDidReceiveTouchEventNotification;
extern NSString * const SVProgressHUDDidTouchDownInsideNotification;
extern NSString * const SVProgressHUDWillDisappearNotification;
extern NSString * const SVProgressHUDDidDisappearNotification;
extern NSString * const SVProgressHUDWillAppearNotification;
extern NSString * const SVProgressHUDDidAppearNotification;

extern NSString * const SVProgressHUDStatusUserInfoKey;

與extern相關(guān)的還有const,static等。下面擴(kuò)展一下

  • const 最好理解牢裳,修飾的東西不能被修改.指針類型根據(jù)位置的不同可以理解成3種情況:

  • 常量指針
    初始化之后不能賦值,指向的對(duì)象可以是任意對(duì)象判帮,對(duì)象可變。
    NSString * const pt1;

  • 指向常量的指針
    初始化之后可以賦值晌畅,即指向別的常量,指針本身的值可以修改,指向的值不能修改
    const NSString * pt2;
  • 指向常量的常量指針
    const NSString * const pt3;
  • extern
    等同于c反粥,全局變量的定義,聲明
    extern const NSString * AA;
    定義
    const NSString * AA = @"abc";
  • static
    等同于c郑气,將變量的作用域限定于本文件?
    不同于java C++里面的類變量讳侨,oc沒(méi)有類變量

--

結(jié)論

static
// static變量屬于本類,不同的類對(duì)應(yīng)的是不同的對(duì)象
// static變量同一個(gè)類所有對(duì)象中共享忱嘹,只初始化一次const
// static const變量同static的結(jié)論I,只是不能修改了础米,但是還是不同的對(duì)象
// extern const變量只有一個(gè)對(duì)象组砚,標(biāo)準(zhǔn)的常量的定義方法
// extern的意思就是這個(gè)變量已經(jīng)定義了糟红,你只負(fù)責(zé)用就行了

typedef NS_ENUM

定義常見(jiàn)的枚舉柒爸,注意命令的方式,***Type值的命名方式 ***TypeLight

UI_APPEARANCE_SELECTOR

UI_APPEARANCE_SELECTOR:這個(gè)關(guān)鍵字是外觀屬性都用到的,用一個(gè)例子簡(jiǎn)單說(shuō)一下它的作用。
[[UIBarButtonItem appearance] setTintColor:[UIColor redColor]];可以定制應(yīng)用中所有條形按鈕的顏色為redColor宰睡。沒(méi)有這個(gè)UI_APPEARANCE_SELECTOR之前,只要一個(gè)一個(gè)控件的去修改。也就是通過(guò)UI_APPEARANCE_SELECTOR可以批量設(shè)置控件的顏色了或南。

深入了解可以看看使用UIAppearance協(xié)議自定義視圖

當(dāng)我在做公用組件的時(shí)候,一定要記得把默認(rèn)值是什么要說(shuō)明一下权她。

類方法

類方法主要有兩大類,一種是set***一種是show**步清。前者用于設(shè)置外觀樣式封豪,后者是直接使用的方式。
比如:

  • set**
+ (void)setDefaultStyle:(SVProgressHUDStyle)style;                  // default is SVProgressHUDStyleLight
+ (void)setDefaultMaskType:(SVProgressHUDMaskType)maskType;         // default is SVProgressHUDMaskTypeNone
+ (void)setDefaultAnimationType:(SVProgressHUDAnimationType)type;   // default is SVProgressHUDAnimationTypeFlat
  • show**
+ (void)show;
+ (void)showWithMaskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use show and setDefaultMaskType: instead.")));
+ (void)showWithStatus:(NSString*)status;
+ (void)showWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showWithStatus: and setDefaultMaskType: instead.")));

這里可以注意一下如何標(biāo)示方法過(guò)期的方式maskType __attribute__((deprecated("Use showSuccessWithStatus: and setDefaultMaskType: instead.")))

.m文件

.m文件包含的內(nèi)容是整個(gè)項(xiàng)目中最復(fù)雜或者說(shuō)是最需要梳理與值得學(xué)習(xí)的胯杭。下面重點(diǎn)介紹下.m文件里面的內(nèi)容鸽心。

常量的定義

關(guān)于define和statc const定義常量的區(qū)別太闺,這里就不講了最住。主要是提醒一下,盡量用statci const來(lái)定義更符合風(fēng)格吧。比如
static const CGFloat SVProgressHUDParallaxDepthPoints = 10;

readonly及getter的使用

雖然這樣的用法有些麻煩履腋,對(duì)于有強(qiáng)迫癥的程序員還是蠻推薦這種寫(xiě)法的

@property (nonatomic, readonly, getter = isClear) BOOL clear;

getter方法

- (BOOL)isClear {
    return (self.defaultMaskType == SVProgressHUDMaskTypeClear || 
    self.defaultMaskType == SVProgressHUDMaskTypeNone);
}

事先定義好私有方法,也就是外界不能直接調(diào)用的實(shí)例方法

這種習(xí)慣能夠快速的了解整個(gè)類一共有哪些方法以及方法歸類等载矿。比如:

- (void)setStatus:(NSString*)status;
- (void)setFadeOutTimer:(NSTimer*)timer;

- (void)registerNotifications;
- (NSDictionary*)notificationUserInfo;

- (void)positionHUD:(NSNotification*)notification;
- (void)moveToPoint:(CGPoint)newCenter rotateAngle:(CGFloat)angle;

- (void)overlayViewDidReceiveTouchEvent:(id)sender forEvent:(UIEvent*)event;

這樣比較有條理,私有方法的定義是在類的擴(kuò)展里面。

使用單例

常見(jiàn)的一些關(guān)于UI的第三方都是通過(guò)類方法調(diào)用迫摔,而且全局可以只用一個(gè)實(shí)例對(duì)象來(lái)維護(hù)就可以了。

+ (SVProgressHUD*)sharedView {
    static dispatch_once_t once;
    
    static SVProgressHUD *sharedView;
#if !defined(SV_APP_EXTENSIONS)
    // 創(chuàng)建單例對(duì)象
    dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[[UIApplication sharedApplication] delegate] window].bounds]; });
#else
    dispatch_once(&once, ^{ sharedView = [[self alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; });
#endif
    return sharedView;
}

show方法參數(shù)化

關(guān)于方法的參數(shù)化說(shuō)白了就是為了實(shí)現(xiàn)代碼的復(fù)用。因?yàn)橛写嬖谙嗤倪壿嬣瓤穑瑒t把相同部分抽離出來(lái),不同的部分通過(guò)傳入不同參數(shù)來(lái)控制來(lái)達(dá)到代碼的復(fù)用翎猛。在實(shí)際工作中,這一點(diǎn)也非常重要遗座。

經(jīng)過(guò)整理懊烤,最終得出所有調(diào)用show**方法最終調(diào)用的只有兩個(gè)個(gè)方法+ (void)showProgress:(float)progress status:(NSString*)status+ (void)showImage:(UIImage*)image status:(NSString*)status

  • showProgress:(float)progress status:(NSString*)status

當(dāng)顯示的是無(wú)限旋轉(zhuǎn)提示的時(shí)候,會(huì)傳入progrerss = -1來(lái)區(qū)別顯示進(jìn)度的樣式日麸。

+ (void)showWithStatus:(NSString*)status {
    [self sharedView];
    [self showProgress:SVProgressHUDUndefinedProgress status:status];
}

這里的SVProgressHUDUndefinedProgress其實(shí)是一個(gè)常量寄啼。其定義為static const CGFloat SVProgressHUDUndefinedProgress = -1;

這里需要提一提的是逮光,設(shè)置遮罩樣式?jīng)]有通過(guò)參數(shù)傳遞來(lái)設(shè)置而是通過(guò)設(shè)置屬性的方式來(lái)做的。

+ (void)showProgress:(float)progress maskType:(SVProgressHUDMaskType)maskType {
    SVProgressHUDMaskType existingMaskType = [self sharedView].defaultMaskType;
    [self setDefaultMaskType:maskType];
    [self showProgress:progress];
    // 顯示完之后回到默認(rèn)的遮罩樣式
    [self setDefaultMaskType:existingMaskType];
}

我簡(jiǎn)單分析了一下不通過(guò)參數(shù)來(lái)傳遞遮罩樣式的原因應(yīng)該是為了每次顯示完之后保證下一次遮罩的樣式依然是默認(rèn)的樣式墩划√楦眨可以看到每次調(diào)用完show**之后都會(huì)把mask恢復(fù)到默認(rèn)值。

  • + (void)showImage:(UIImage*)image status:(NSString*)status

這個(gè)方法是會(huì)自動(dòng)消失show**最終會(huì)調(diào)用的方法乙帮,比如

+ (void)showInfoWithStatus:(NSString*)status;
+ (void)showInfoWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showInfoWithStatus: and setDefaultMaskType: instead.")));
+ (void)showSuccessWithStatus:(NSString*)status;
+ (void)showSuccessWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showSuccessWithStatus: and setDefaultMaskType: instead.")));
+ (void)showErrorWithStatus:(NSString*)status;
+ (void)showErrorWithStatus:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showErrorWithStatus: and setDefaultMaskType: instead.")));

// shows a image + status, use 28x28 white PNGs
+ (void)showImage:(UIImage*)image status:(NSString*)status;
+ (void)showImage:(UIImage*)image status:(NSString*)status maskType:(SVProgressHUDMaskType)maskType __attribute__((deprecated("Use showImage:status: and setDefaultMaskType: instead.")));

UIAccessibility

UIAccessibility協(xié)議用于讓外界程序了解到自己身的執(zhí)情情況杜漠。Accessibility是一個(gè)交互協(xié)議,基于查詢<->應(yīng)答察净,通知<->監(jiān)聽(tīng)模型的協(xié)議驾茴。外部程序通過(guò)查詢來(lái)獲取APP應(yīng)答,從而了解程序氢卡。另外通過(guò)監(jiān)聽(tīng)來(lái)自APP的消息锈至,來(lái)通知用戶當(dāng)前狀態(tài)。

  • 1.常用的協(xié)議與元素包括:

UIAccessibility, protocol译秦,核心協(xié)議峡捡。
UIAccessibilityAction,protocol筑悴,添加行為的協(xié)議们拙。 UIAccessibilityElement, class。
UIAccessibilityContainer阁吝,protocol砚婆,容器協(xié)議。

  • 2.常用函數(shù) UIAccessibilityPostNotification求摇。

可以看到SVProgressHUD支持UIAccessibility

     // Accessibility support
    self.accessibilityIdentifier = @"SVProgressHUD";
    self.accessibilityLabel = @"SVProgressHUD";
    self.isAccessibilityElement = YES;

看一下官方介紹

/*
 UIAccessibility
 
 UIAccessibility is implemented on all standard UIKit views and controls so
 that assistive applications can present them to users with disabilities.
 
 Custom items in a user interface should override aspects of UIAccessibility
 to supply details where the default value is incomplete.
 
 For example, a UIImageView subclass may need to override accessibilityLabel,
 but it does not need to override accessibilityFrame.
 
 A completely custom subclass of UIView might need to override all of the
 UIAccessibility methods except accessibilityFrame.
 */

showProgress:(float)progress status:(NSString*)status

我們都知道關(guān)于UI的操作都需要放在主線程中射沟。一般會(huì)通過(guò)GCD的方式如下:

dispatch_async(dispatch_get_main_queue(), ^{

    });

但是SVProgressHUD里面用的是NSOperation來(lái)實(shí)現(xiàn)的,

[[NSOperationQueue mainQueue] addOperationWithBlock:^{

    }];

這也給了我們另外一種方式回到主線程与境。??

strong & weak

 __weak SVProgressHUD *weakSelf = self;

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        if(strongSelf){
            // Update / Check view hierachy to ensure the HUD is visible
            [strongSelf updateViewHierachy];
            ......

上面的代碼是從源碼中摘的,可以看到為了防止循環(huán)引用在block外面用了__weak SVProgressHUD *weakSelf = self;而在block里面用了__strong SVProgressHUD *strongSelf = weakSelf;最開(kāi)始了解這種用法是從AFNetWorking源碼中看到了猖吴。為了保證在執(zhí)行block的時(shí)候weakSelf還存在(因?yàn)榭赡軙?huì)延遲調(diào)用)摔刁,所以需要在block里面用__strong在修飾一次weakSelf.

視圖顯示的邏輯

為了更好地說(shuō)明問(wèn)題,我直接在源碼中加注釋方便說(shuō)明海蔽。

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        // 邏輯嚴(yán)謹(jǐn)共屈,需要判斷是否存在
        if(strongSelf){
            // 更新并且檢查視圖層次確保SVProgressHUD可見(jiàn)
            [strongSelf updateViewHierachy];
            
            // 重置imageView和消失時(shí)間。防止之前調(diào)用過(guò)党窜,使用上次存在的樣式設(shè)置
            strongSelf.imageView.hidden = YES;
            strongSelf.imageView.image = nil;
            
            if(strongSelf.fadeOutTimer) {
                strongSelf.activityCount = 0;
            }
            strongSelf.fadeOutTimer = nil;
            
            
            // 更新statusLabel顯示的內(nèi)容和顯示的進(jìn)度
            strongSelf.statusLabel.text = status;
            strongSelf.progress = progress;
            
            // 根據(jù)progersss的值來(lái)確定正確的樣式拗引,當(dāng)progress>=0的時(shí)候,顯示進(jìn)度樣式幌衣,當(dāng)progress = -1的時(shí)候?yàn)闊o(wú)限旋轉(zhuǎn)的樣式
            if(progress >= 0) {
                // 防止上次為無(wú)限旋轉(zhuǎn)的樣式導(dǎo)致重疊
                [strongSelf cancelIndefiniteAnimatedViewAnimation];
                
                // 添加進(jìn)度視圖到hudview上矾削,并且設(shè)置當(dāng)前及大怒
                [strongSelf.hudView addSubview:strongSelf.ringView];
                [strongSelf.hudView addSubview:strongSelf.backgroundRingView];
                strongSelf.ringView.strokeEnd = progress;
                
                // 更新activityCount
                if(progress == 0) {
                    strongSelf.activityCount++;
                }
            } else {
                // 防止上次為進(jìn)度的樣式導(dǎo)致重疊
                [strongSelf cancelRingLayerAnimation];
                
                // 增加無(wú)限旋轉(zhuǎn)視圖到hudview上
                [strongSelf.hudView addSubview:strongSelf.indefiniteAnimatedView];
                if([strongSelf.indefiniteAnimatedView respondsToSelector:@selector(startAnimating)]) {
                    [(id)strongSelf.indefiniteAnimatedView startAnimating];
                }
                
                strongSelf.activityCount++;
            }
            
            // 顯示提示的文字信息
            [strongSelf showStatus:status];
        }
    }];

大致的思路理清了壤玫。把顯示文字的邏輯和顯示進(jìn)度與旋轉(zhuǎn)的路基分開(kāi)來(lái)實(shí)現(xiàn)的。因?yàn)轱@示文字的邏輯是公有的哼凯。

上面代碼中需要注意的是有一個(gè)更新視圖層次的方法[strongSelf updateViewHierachy].因?yàn)橐晥D層次很有可能在運(yùn)行的時(shí)候被改變欲间,比如通過(guò)runtime。所以每次都需要更新一下視圖層次断部,保證能夠顯示出來(lái)猎贴。

更新提示文字showStatus:(NSString*)status

還是根據(jù)代碼來(lái)看吧

- (void)showStatus:(NSString*)status {
    // 更新frame及位置,因?yàn)閒rame是更加status來(lái)確定的而postion是根據(jù)參數(shù)控制的蝴光。
    [self updateHUDFrame];
    [self positionHUD:nil];
    
    // 更新 accesibilty 和是否可以點(diǎn)擊
    if(self.defaultMaskType != SVProgressHUDMaskTypeNone) {
        self.overlayView.userInteractionEnabled = YES;
        self.accessibilityLabel = status;
        self.isAccessibilityElement = YES;
    } else {
        self.overlayView.userInteractionEnabled = NO;
        self.hudView.accessibilityLabel = status;
        self.hudView.isAccessibilityElement = YES;
    }
    
    self.overlayView.backgroundColor = [UIColor clearColor];
    
    // 根據(jù)alpha值判斷是是否可見(jiàn)
    if(self.alpha != 1.0f || self.hudView.alpha != 1.0f) {
        // 如果之前不可見(jiàn)則發(fā)出SVProgressHUDWillAppearNotification通知梗脾,告訴馬上顯示
        [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDWillAppearNotification
                                                            object:self
                                                          userInfo:[self notificationUserInfo]];
        
        // 縮放效果
        self.hudView.transform = CGAffineTransformScale(self.hudView.transform, 1.3, 1.3);

        // 處理初始值處理iOS7及以上不會(huì)響應(yīng)透明度改變的UIToolbar
        self.alpha = 0.0f;
        self.hudView.alpha = 0.0f;
        
        // 定義動(dòng)畫(huà)block及完成動(dòng)畫(huà)block
        __weak SVProgressHUD *weakSelf = self;
        
        __block void (^animationsBlock)(void) = ^{
            __strong SVProgressHUD *strongSelf = weakSelf;
            if(strongSelf) {
                // Shrink HUD to finish pop up animation
                strongSelf.hudView.transform = CGAffineTransformScale(strongSelf.hudView.transform, 1/1.3f, 1/1.3f);
                strongSelf.alpha = 1.0f;
                strongSelf.hudView.alpha = 1.0f;
            }
        };
        
        __block void (^completionBlock)(void) = ^{
            __strong SVProgressHUD *strongSelf = weakSelf;
            if(strongSelf) {
                /// Register observer <=> we now have to handle orientation changes etc.
                [strongSelf registerNotifications];
                
                // Post notification to inform user
                [[NSNotificationCenter defaultCenter] postNotificationName:SVProgressHUDDidAppearNotification
                                                                    object:strongSelf
                                                                  userInfo:[strongSelf notificationUserInfo]];
            }
            
            // 更新 accesibilty
            UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);
            UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, status);
        };
        
        if (self.fadeInAnimationDuration > 0) {
            // 如果設(shè)置了動(dòng)畫(huà)時(shí)間則進(jìn)行動(dòng)畫(huà)效果
            [UIView animateWithDuration:self.fadeInAnimationDuration
                                  delay:0
                                options:(UIViewAnimationOptions) (UIViewAnimationOptionAllowUserInteraction | UIViewAnimationCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState)
                             animations:^{
                                 animationsBlock();
                             } completion:^(BOOL finished) {
                                 completionBlock();
                             }];
        } else {
        // 未設(shè)置動(dòng)畫(huà)時(shí)間
            animationsBlock();
            completionBlock();
        }
        
        // 完成了更新視圖層次,視圖的frame以及視圖的各種屬性之后棘伴,告訴系統(tǒng)稍微進(jìn)行重繪
        [self setNeedsDisplay];
    }
}

以后再寫(xiě)吧.....

真的沒(méi)想到自己把看懂的東西一個(gè)個(gè)寫(xiě)出來(lái)是這么的麻煩魂拦,讓自己寫(xiě)出來(lái)進(jìn)行分享的時(shí)候真的好費(fèi)時(shí)間。這篇文字自己也是抽空一點(diǎn)一點(diǎn)拼湊起來(lái)的做瞪。

自己也一直想做一個(gè)源碼分析的系列对粪,前不久看到了github上已經(jīng)有人有類似的項(xiàng)目。有些觸動(dòng)装蓬,有時(shí)候自己想法很多而實(shí)際動(dòng)手去做的時(shí)間卻很少著拭。總的說(shuō)來(lái)自己總結(jié)出牍帚,分析開(kāi)源代碼不難儡遮,難的是如果寫(xiě)好分析文章。

可能分析得有些粗糙暗赶。不過(guò)也是自己一個(gè)字一個(gè)字寫(xiě)出來(lái)的鄙币。堅(jiān)持下去吧!蹂随!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末十嘿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子岳锁,更是在濱河造成了極大的恐慌绩衷,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,835評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件激率,死亡現(xiàn)場(chǎng)離奇詭異咳燕,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)乒躺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)招盲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人嘉冒,你說(shuō)我怎么就攤上這事曹货∨胤保” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,481評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵控乾,是天一觀的道長(zhǎng)么介。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蜕衡,這世上最難降的妖魔是什么壤短? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,303評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮慨仿,結(jié)果婚禮上久脯,老公的妹妹穿的比我還像新娘。我一直安慰自己镰吆,他們只是感情好帘撰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,375評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著万皿,像睡著了一般摧找。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上牢硅,一...
    開(kāi)封第一講書(shū)人閱讀 49,729評(píng)論 1 289
  • 那天蹬耘,我揣著相機(jī)與錄音,去河邊找鬼减余。 笑死综苔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的位岔。 我是一名探鬼主播如筛,決...
    沈念sama閱讀 38,877評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼抒抬!你這毒婦竟也來(lái)了杨刨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,633評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤擦剑,失蹤者是張志新(化名)和其女友劉穎拭嫁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體抓于,經(jīng)...
    沈念sama閱讀 44,088評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,443評(píng)論 2 326
  • 正文 我和宋清朗相戀三年浇借,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捉撮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,563評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妇垢,死狀恐怖巾遭,靈堂內(nèi)的尸體忽然破棺而出肉康,到底是詐尸還是另有隱情,我是刑警寧澤灼舍,帶...
    沈念sama閱讀 34,251評(píng)論 4 328
  • 正文 年R本政府宣布吼和,位于F島的核電站,受9級(jí)特大地震影響骑素,放射性物質(zhì)發(fā)生泄漏炫乓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,827評(píng)論 3 312
  • 文/蒙蒙 一献丑、第九天 我趴在偏房一處隱蔽的房頂上張望末捣。 院中可真熱鬧,春花似錦创橄、人聲如沸箩做。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,712評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)邦邦。三九已至,卻和暖如春醉蚁,著一層夾襖步出監(jiān)牢的瞬間燃辖,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,943評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工馍管, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留郭赐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,240評(píng)論 2 360
  • 正文 我出身青樓确沸,卻偏偏與公主長(zhǎng)得像捌锭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子罗捎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,435評(píng)論 2 348

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

  • *面試心聲:其實(shí)這些題本人都沒(méi)怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來(lái)就是把...
    Dove_iOS閱讀 27,128評(píng)論 29 470
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉观谦,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評(píng)論 0 9
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)桨菜,斷路器豁状,智...
    卡卡羅2017閱讀 134,626評(píng)論 18 139
  • 從小到大明明很脆弱的內(nèi)心卻總要故裝強(qiáng)大泻红,明明也需要?jiǎng)e人的幫助卻總是告訴自己忍忍就過(guò)了,所以無(wú)論什么時(shí)候只要他人能找...
    小鄧同學(xué)閱讀 434評(píng)論 0 0
  • 人呢總有忽如其來(lái)的奇思妙想霞掺,似乎等了這么久就是在尋找一個(gè)可以如此暢所欲言谊路,直抒己意的平臺(tái)。不需要咬文嚼字菩彬,只求一個(gè)...
    踏浪的小腳丫閱讀 276評(píng)論 0 0