MBProgressHUD详解(二)
外观
先来一发图片,MBProgressHUD整体布局如上图,图片手工画的比较丑,将就着看吧~~~
1.backgroundView 整个背景图层,可以通过MBBackgroundView的style属性设置样式。跟系统有关
2.bezel视图,提供indicator、label、detailLabel、button的背景,用来突出显示 这个可以通过animationType属性设置动画效果,其实也是可选的,当mode值为MBProgressHUDModeText时,只有文本提示
3.indicator控件,指示器显示进度情况 这个视图由我们设定的mode属性决定,可以是菊花、进度条,也可以是我们自定义的视图
4.label控件,显示简要提示 (可选)
5.detailLabel控件,显示详细提示 (可选)
6.button按钮控件,提供中间控制动作,注意:button这个按钮只有在添加响应事件时才显示 (可选)
style、mode、animationType可以看MBProgressHUD.h文件中的枚举,在MBProgressHUD详解(一)中介绍
MBProgressHUD对象的绘制
我们通过头文件可以看到,MBProgressHUD提供了三个类函数
//创建一个新的HUD,并把它显示在view之上,还可以设置是否以动画的形式,此外,该函数返回一个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;
常用的也就第一个函数+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated;
推荐使用,这也是github中实例中使用的
此外也提供了几个是实例函数
//以view为基准创建初始化一个HUD对象,为HUD的初始化构造函数
- (instancetype)initWithView:(UIView *)view;
//显示HUD控件,此函数应该在主线程中调用
- (void)showAnimated:(BOOL)animated;
//隐藏HUD控件,animated控制是否显示动画。对应于- (void)showAnimated:(BOOL)animated;
- (void)hideAnimated:(BOOL)animated;
//在delay时间之后隐藏HUD,animated控制显示动画与否,delay控制延迟时间
- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay;
比较常用的有两个个函数- (void)hideAnimated:(BOOL)animated;
和- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay;
接下来我们就根据程序的执行过程来一步一步分析一下代码
初始化
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [[self alloc] initWithView:view]; //创建并初始化MBProgressHUD对象
hud.removeFromSuperViewOnHide = YES; //设置removeFromSuperViewOnHide属性
[view addSubview:hud];
[hud showAnimated:animated]; //添加到父View中,并显示
return hud; //返回自身
}
这个函数主要调用了两个方法- (id)initWithView:(UIView *)view
和- (void)showAnimated:(BOOL)animated
函数- (id)initWithView:(UIView *)view
最终调用- (void)commonInit
初始化设置一些属性
- (void)commonInit {
//设置默认属性
// 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]; //设置所需的子view 注意此时各个view的位置大小还未确定
[self updateIndicators]; //设置指示器样式
[self registerForNotifications]; //注册系统通知
}
这个函数里面又调用了三个函数setupViews
、updateIndicators
、registerForNotifications
,这三个函数的主要作用上面代码注释都说明了。特别注意的是setupViews
函数返回时,各个view的位置大小还未确定。这里我们主要介绍前面两个函数setupViews和updateIndicators,上代码,基本的地方都有注释
- (void)setupViews {
UIColor *defaultColor = self.contentColor;
//创建背景视图
MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds];
backgroundView.style = MBProgressHUDBackgroundStyleSolidColor;
backgroundView.backgroundColor = [UIColor clearColor];
//自动调整view的宽度,保证左边距和右边距不变 | 自动调整view的高度,以保证上边距和下边距不变
backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
backgroundView.alpha = 0.f;
[self addSubview:backgroundView];
_backgroundView = backgroundView;
//创建小方块背景视图
MBBackgroundView *bezelView = [MBBackgroundView new];
//代码层面使用Autolayout,需要对使用的View的translatesAutoresizingMaskIntoConstraints的属性设置为NO
bezelView.translatesAutoresizingMaskIntoConstraints = NO;
bezelView.layer.cornerRadius = 5.f;
bezelView.alpha = 0.f;
[self addSubview:bezelView];
_bezelView = bezelView;
[self updateBezelMotionEffects]; //设置视差效果
//创建label信息标签,提示简要信息
UILabel *label = [UILabel new];
//取消文字大小自适应
label.adjustsFontSizeToFitWidth = NO;
label.textAlignment = NSTextAlignmentCenter;
label.textColor = defaultColor;
label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize];
//告诉系统渲染器view是否不透明,设置YES可以加快渲染,默认为YES,如果设置了alpha值,应该设置为NO
label.opaque = NO;
label.backgroundColor = [UIColor clearColor];
_label = label;
//创建detailsLabel信息标签,提示详细信息
UILabel *detailsLabel = [UILabel new];
//取消文字大小自适应
detailsLabel.adjustsFontSizeToFitWidth = NO;
detailsLabel.textAlignment = NSTextAlignmentCenter;
detailsLabel.textColor = defaultColor;
detailsLabel.numberOfLines = 0;
detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize];
//告诉系统渲染器view是否不透明,设置YES可以加快渲染,默认为YES,如果设置了alpha值,应该设置为NO
detailsLabel.opaque = NO;
detailsLabel.backgroundColor = [UIColor clearColor];
_detailsLabel = detailsLabel;
//创建事件响应按钮
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的属性设置为NO,以使用Autolayout
view.translatesAutoresizingMaskIntoConstraints = NO;
//当试图变化时,设置水平和垂直方向变化的优先权
//这是设置每一个view的优先权都是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;
}
由代码我们可以看出,这个函数首先创建了backgroundView、bezelView、label、detailsLabel、button,中间使用了一个for循环把label、detailsLabel、button添加到bezelView视图中,最后还创建了顶部视图和底部视图,不过默认是隐藏的。
最初看这里的时候有个小疑惑,这里明明创建了button但是如果没有设置button属性,这个按钮是不会显示的。原来这里重新写了一个UIbutton的子类MBProgressHUDRoundedButton,这个子类重写了一个函数- (CGSize)intrinsicContentSize
,这个函数也就是控件的内置大小。比如UILabel,UIButton等控件,他们都有自己的内置大小。我们可以重写这个函数设置控件的大小。。
- (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(没有大小)。
接下来我们看一下另一个函数
- (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) { //系统自带的指示器
if (!isActivityIndicator) { //如果目前指示器不是UIActivityIndicatorView,则移除之前的indicator创建新的
// 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) { //圆环指示器
[(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的属性设置为NO,以使用Autolayout
indicator.translatesAutoresizingMaskIntoConstraints = NO;
self.indicator = indicator;
if ([indicator respondsToSelector:@selector(setProgress:)]) {
[(id)indicator setValue:@(self.progress) forKey:@"progress"]; //设置进度条的数值
}
//当试图变化时,设置水平和垂直方向变化的优先权
[indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal];
[indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical];
[self updateViewsForColor:self.contentColor]; //设置控件颜色的颜色
[self setNeedsUpdateConstraints]; //更新布局,系统自动调用updateConstraints
}
这个函数主要是用来设置indicator指示器,根据mode的属性显示不同的形式,具体的可以参看代码注释,系统提供的菊花形状的指示器我们就不过多说明了,一会我们着重介绍一下MBProgressHUDModeDeterminateHorizontalBar模式。
再次回到这个函数中,这个函数最后调用的是setNeedsUpdateConstraints函数,这个函数会自动调用updateConstraints,这个函数主要作用是更新一下各个控件的布局,代码如下:
//系统自动调用
- (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是插入到指定 索引的前面,即插入到数组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居中显示,如果设置了偏移offset,则同时设置偏移
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
//如果定义了最小的宽高,这设置其最小大小
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设置
// 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里面的子视图大小设置
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的技术,如果需要深入了解的可以自行查阅文档。。
至此,PUD对象的创建工作就完成,现在我们来看一下指示器的几种形式,通过代码可知,PUD提供了几种指示器的形式菊花、棒状进度条,圆饼/圆环进度条。在这里我们着重介绍一下棒状进度条。
棒状进度条是MBBarProgressView这个类实现的,通过- (void)drawRect:(CGRect)rect
这个函数绘制。
//设置棒状进度条背景
- (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
// 设置进度条根据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);
}
}
这个函数虽然很长,但是它主要绘制了两个部分,进度条最外面的椭圆环和内部的进度条,内部的进度条根据其progress实现长短变化。
显示
PUD对象的显示只有一个函数- (void)showAnimated:(BOOL)animated
,代码如下:
//根据参数显示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
//如果设置了宽限时间graceTime,则延迟显示,否则直接显示
if (self.graceTime > 0.0) {
//创建定时器,并把它加入到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];
}
}
这个函数有个需要注意的地方:此函数必须在主线程中执行。
消失
PUD对象提供了两个隐藏的函数- (void)hide:(BOOL)animated
和- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay
,通过名字就额可以看出第二个函数是延迟delay时间在隐藏消失,
- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay {
//创建定时器,并把它加入到NDRunLoop中
NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.hideDelayTimer = timer;
}
只是简单的创建一个定时器,并把定时器加入到NDRunLoop中,延迟delay执行handleHideTimer:
函数。
这两个函数最后都调用函数- (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
//如果设置了最小显示时间,则执行此步骤,否则直接隐藏
if (self.minShowTime > 0.0 && self.showStarted) {
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
if (interv < self.minShowTime) {
//创建定时器,并把它加入到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];
}
这个函数同样设置了一个定时器,根据minShowTime属性,控制PUD显示的时机。
最后的最后,显示和隐藏都统一到一个函数中- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion
,在这个函数中,我们可以设置一些PUD对象出现和隐藏时的动画效果,具体请看代码注释。
//消失或出现时的伸缩效果,以及透明度
- (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];
}
删除
经过以上步骤,PUD经历了创建、显示和隐藏,但是对象并没消失,只是隐藏了,变透明了。所以还需一个函数处理一下后续动作- (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 delegate = self.delegate;
if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
[delegate performSelector:@selector(hudWasHidden:) withObject:self];
}
}
这个函数如果removeFromSuperViewOnHide属性为YES,则将自己从父视图上移除,如果有completionBlock回调函数,则执行回调,如果实现了代理并实现了代理方法,则执行代理方法。
到这里整个的执行流程差不多就算结束了,剩下的清理工作都是系统自动调用,就不过多说明了。。
写的比较乱,谢谢你们能够忍着看完,如果有什么错误或者不恰当的地方,请不留情面的指出来,共同交流,共同进步。。。