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步。谤逼。贵扰。