SVProgressHUD

[TOC]

簡(jiǎn)介

SVProgressHUB是iOS上的的一款loading輕量級(jí)美觀的加載框酒贬。
首先來看下結(jié)構(gòu)目錄

SVIndefiniteAnimatedView 是無限旋轉(zhuǎn)的菊花視圖
SVProgressAnimatedView 是進(jìn)度條視圖
SVRadialGradientLayer 是漸變模式下漸變的背景Layer

使用

顯示

SVProgressHUD提供了顯示加載圖便利方法,而且?guī)缀跞穷惙椒ù浠簦陂_發(fā)中能方便的使用同衣。如

  • showWithStatus 顯示加載,并顯示文本
  • showProgress 顯示加載壶运,并展示當(dāng)前進(jìn)度條
  • showInfoWithStatus 顯示使用Info狀態(tài)的圖片的加載框來替代菊花或者進(jìn)度條
  • showSuccessWithStatus 顯示success狀態(tài)的圖片(一個(gè)勾)的加載框來替代菊花或者進(jìn)度條
  • showErrorWithStatus 顯示error狀態(tài)的圖片(一個(gè)x)的加載框來替代菊花或者進(jìn)度條

除此之外你也可以設(shè)置自己的圖片耐齐,使用+ (void)showImage:(UIImage*)image status:(NSString*)status;

消失

SVProgressHUD提供了幾種方式來顯示一個(gè)Loding框

  • dismiss 直接關(guān)閉一個(gè)加載Hub
  • dismissWithDelay 延遲關(guān)閉一個(gè)Hub
  • dismissWithCompletion 關(guān)閉Hub帶一個(gè)完成的Block
  • dismissWithDelay:completion 延遲關(guān)閉Hub并帶一個(gè)完成的Block

可以看出SVProgressHUD提供了簡(jiǎn)單明了的API,使用便利的類方法讓我們快速在代碼中使用

深入源碼

顯示過程

SVProgressHUD 比較簡(jiǎn)單易懂
在眾多的類方法下面,也實(shí)現(xiàn)相應(yīng)的實(shí)例方法

//簡(jiǎn)單版本
- (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;

- (void)showProgress:(float)progress status:(NSString*)status;
- (void)showImage:(UIImage*)image status:(NSString*)status duration:(NSTimeInterval)duration;
- (void)showStatus:(NSString*)status;

- (void)dismiss;
- (void)dismissWithDelay:(NSTimeInterval)delay completion:(SVProgressHUDDismissCompletion)completion;
@end

如何將Hub展示到屏幕上埠况,首先我們從showWithStatus方法往里看耸携。
內(nèi)部實(shí)現(xiàn)

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

在內(nèi)部實(shí)現(xiàn)了一個(gè)單例,這也不難理解為什么可以在內(nèi)方法中快速的顯示一個(gè)Hub

+ (SVProgressHUD*)sharedView {
    static dispatch_once_t once;
    
    static SVProgressHUD *sharedView;
#if !defined(SV_APP_EXTENSIONS)
    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;
}

之后使用這個(gè)單例視圖來完成Hub的顯示辕翰,那么showProgress這個(gè)方法又做什么呢夺衍?首先我們看下簡(jiǎn)化版代碼

- (void)showProgress:(float)progress status:(NSString*)status {
    __weak SVProgressHUD *weakSelf = self;
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        __strong SVProgressHUD *strongSelf = weakSelf;
        if(strongSelf){
             //更新視圖層級(jí),將SV放到當(dāng)前window最前面
            [strongSelf updateViewHierachy];
            //判斷如果是進(jìn)度條喜命,使用進(jìn)度條View
            if(progress >= 0) {                
                // Add ring to HUD and set progress
                [strongSelf.hudView addSubview:strongSelf.ringView];
                [strongSelf.hudView addSubview:strongSelf.backgroundRingView];
                strongSelf.ringView.strokeEnd = progress;
            } else {
                    
                
                // 使用無限旋轉(zhuǎn)的菊花模式
                [strongSelf.hudView addSubview:strongSelf.indefiniteAnimatedView];
            }
            // Show
            [strongSelf showStatus:status];
        }
    }];
}

可以看出沟沙,SV為了將顯示操作放到主線程使用了[NSOperationQueue mainQueue]來確保在主線程操作UI,
updateViewHierachy方法中壁榕,SV會(huì)尋找提供Hub顯示的Window矛紫,并且將自己添加到上面。使用手法為:

 NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
        for (UIWindow *window in frontToBackWindows) {
            BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
            BOOL windowIsVisible = !window.hidden && window.alpha > 0;
            BOOL windowLevelNormal = window.windowLevel == UIWindowLevelNormal;
            
            if(windowOnMainScreen && windowIsVisible && windowLevelNormal) {
                [window addSubview:self.overlayView];
                break;
            }
        }

找出當(dāng)前的windowLevelNormal,并且是顯示的UI牌里,將自己顯示在其之上颊咬,但是也有點(diǎn)缺點(diǎn),就是如果需要在自定義的window上面顯示Hub將無能為力牡辽。
找到window之后喳篇,生成菊花或者進(jìn)度條View,然后設(shè)置label态辛,計(jì)算出大小麸澜,然后完成顯示

動(dòng)畫的繪制

SVIndefiniteAnimatedView是無限旋轉(zhuǎn)的View。其中使用CASharpLayer和LayerMask來完成一個(gè)旋轉(zhuǎn)動(dòng)畫奏黑,使用一張漸變的圖片炊邦,設(shè)置其mask屬性,然后加上旋轉(zhuǎn)動(dòng)畫攀涵,來形成一個(gè)漂亮的加載動(dòng)畫
動(dòng)畫繪制代碼

        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;
        [_indefiniteAnimatedLayer.mask 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"];

圓環(huán)動(dòng)畫比較簡(jiǎn)單铣耘,原理是使用CAShareLayer,并且設(shè)置的endPath來完成一個(gè)由progress驅(qū)動(dòng)的進(jìn)度條動(dòng)畫

 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;

消失過程

消失調(diào)用的是dismissWithDelay:completion方法
消失過程和顯示過程相反以故,大致是

  • 在mainQueue中添加一個(gè)Block的operation(在主線程更改UI)
  • 將自己從SuperView中刪除蜗细,
  • 如果指定了延遲,那么設(shè)置一個(gè)timer
  • 在消失中如果指定動(dòng)畫怒详,那么加一個(gè)fade的動(dòng)畫來進(jìn)行消失

小結(jié)

  1. SV將View添加到Window上
  2. SV使用NSOperationQueue 來確保自己在主線程修改UI
  3. SV的動(dòng)畫使用layer來繪制

優(yōu)點(diǎn):API簡(jiǎn)單明了炉媒,調(diào)用方便
缺點(diǎn):默認(rèn)在APP的UIWindow,不好定制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昆烁,一起剝皮案震驚了整個(gè)濱河市吊骤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌静尼,老刑警劉巖白粉,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件传泊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡鸭巴,警方通過查閱死者的電腦和手機(jī)眷细,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鹃祖,“玉大人溪椎,你說我怎么就攤上這事√窨冢” “怎么了校读?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長祖能。 經(jīng)常有香客問我歉秫,道長,這世上最難降的妖魔是什么芯杀? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任端考,我火速辦了婚禮雅潭,結(jié)果婚禮上揭厚,老公的妹妹穿的比我還像新娘。我一直安慰自己扶供,他們只是感情好筛圆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著椿浓,像睡著了一般太援。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扳碍,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天提岔,我揣著相機(jī)與錄音,去河邊找鬼笋敞。 笑死碱蒙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的夯巷。 我是一名探鬼主播赛惩,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼趁餐!你這毒婦竟也來了喷兼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤后雷,失蹤者是張志新(化名)和其女友劉穎季惯,沒想到半個(gè)月后吠各,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勉抓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年走孽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片琳状。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡磕瓷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出念逞,到底是詐尸還是另有隱情困食,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布翎承,位于F島的核電站硕盹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叨咖。R本人自食惡果不足惜瘩例,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望甸各。 院中可真熱鬧垛贤,春花似錦、人聲如沸趣倾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽儒恋。三九已至善绎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诫尽,已是汗流浹背禀酱。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牧嫉,地道東北人剂跟。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像驹止,于是被迫代替她去往敵國和親浩聋。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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