最近项目需要添加提示控件(HUD
),想着参考一下别人的源码再封装,然后就找了MBProgressHUD,然后记录下。
框架使用起来很简单:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
源码
-
MBProgressHUD
就是展示一个封装的UIView
,大小根据传入的view
铺满(由于背景铺满,所以可控制展示时不能点击其他视图),于是想要什么提示效果便可以自己实现(MBProgressHUD
可以修改customView
和label
):
@interface MBProgressHUD : UIView
@implementation MBProgressHUD
+ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [[self alloc] initWithView:view];
hud.removeFromSuperViewOnHide = YES;
[view addSubview:hud];//添加
[hud showAnimated:animated];
return hud;
}
- (id)initWithView:(UIView *)view {
NSAssert(view, @"View must not be nil.");
return [self initWithFrame:view.bounds];
}
- (instancetype)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
[self commonInit];
}
return self;
}
- (void)commonInit {
...
[self setupViews];//设置子视图
[self updateIndicators];//设置指示器
[self registerForNotifications];//注册通知
}
- 然后进行展示:
- (void)showAnimated:(BOOL)animated {
...
else {
[self showUsingAnimation:self.useAnimation];
}
}
- (void)showUsingAnimation:(BOOL)animated {
...
if (animated) {
[self animateIn:YES withType:self.animationType completion:NULL];
} else {
self.bezelView.alpha = 1.f;
self.backgroundView.alpha = 1.f;
}
}
- 隐藏时循环获取:
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
MBProgressHUD *hud = [self HUDForView:view];
if (hud != nil) {
hud.removeFromSuperViewOnHide = YES;
[hud hideAnimated:animated];
return YES;
}
return NO;
}
+ (MBProgressHUD *)HUDForView:(UIView *)view {
NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
for (UIView *subview in subviewsEnum) {
if ([subview isKindOfClass:self]) {
MBProgressHUD *hud = (MBProgressHUD *)subview;
if (hud.hasFinished == NO) {
return hud;
}
}
}
return nil;
}
- (void)hideAnimated:(BOOL)animated {
...
[self hideUsingAnimation:self.useAnimation];
}
- (void)hideUsingAnimation:(BOOL)animated {
...
if (animated && self.showStarted) {
self.showStarted = nil;
[self animateIn:NO withType:self.animationType completion:^(BOOL finished) {
[self done];
}];
} else {
self.showStarted = nil;
self.bezelView.alpha = 0.f;
self.backgroundView.alpha = 1.f;
[self done];
}
}
- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion {
...
[UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion];
}
- 最终调用
done
进行移除并进行相应回调:
- (void)done {
[self setNSProgressDisplayLinkEnabled:NO];
if (self.hasFinished) {
self.alpha = 0.0f;
if (self.removeFromSuperViewOnHide) {
[self removeFromSuperview];//移除
}
}
//进行回调
MBProgressHUDCompletionBlock completionBlock = self.completionBlock;
if (completionBlock) {
completionBlock();
}
id<MBProgressHUDDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(hudWasHidden:)]) {
[delegate performSelector:@selector(hudWasHidden:) withObject:self];
}
}

- 与
SVProgressHUD
的对比
SVProgressHUD
类对外提供的都是类方法,其内部实现为一个单例对象。
SVProgressHUD
主要包含三部分:loading视图、提示文本框和背景框,没有详情文本框。
SVProgressHUD
默认提供了正确、错误和信息三种状态视图。
SVProgressHUD
为我们提供了更多的交互操作,包括点击事件、显示事件及隐藏事件。不过这些都是通过通知的形式向外发送,所以我们需要自己去监听这些事件。
SVProgressHUD
中一些loading动画是以Layer
动画的形式来实现的。
-
自己封装
如果自己封装一个简单的HUD
,就是添加一个自定义UIView
控件到页面上方,自己可以在实现对应效果,然后展示或隐藏。
-
我直接添加到
UIWindow
上全局使用,然后用hiden
控制展示和隐藏. -
可能计算自适应
label
会有点麻烦,因为没有用约束. -
然后自己用
UIActivityIndicatorView
作为加载提示器,发现太小,使用transform
变大后通过Debug View Hierarchy
发现菊花图案有右/下边距,没法居中,于是自己使用CAShapeLayer
、UIBezierPath
和CABasicAnimation
写了简陋的提示效果:

- 注意处理同时显示隐藏操作的冲突问题。
网友评论