Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

TongCong/TGGProgressHUD

Repository files navigation

TGGProgressHUD

HUD这类控件在我们的实际开发中很是常用,几乎每个项目中都会大量的用到,用于弹出提示的文字或者图片告知用户当前正在做的事情

系统提供了UIAlertController(iOS8+)这个类,使用它我们可以弹出一部分的提示,但是往往和我们实际需求的是不一样的。所以我们需要自定义弹出视图

HUD这一类工具,MBProgressHUD和SVProgressHUD这两大开源控件技术每个iOS开发者都用过,而且这两个也几乎能全部满足于我们的需求了,但是随着APP的数量多不胜数,加上一些特定的项目,大家都希望自己的APP会有独特的地方或者创新的东西,而这些小小的"创新",就会让我们开发者要多写很多东西了,而当这个创新落在"HUD"上的时候,熟悉了使用三方HUD的开发者就可能需要修改源码了,虽然"MBProgressHUD"在拓展性已经很好了,但是自己写一个HUD何乐而不为

最终的API是这样的:

@interface TCProgressHUD (Public)
/** 提示文字,不会自动隐藏*/
+ (void)showStatus:(NSString *)status;
/** 提示文字,会自动隐藏*/
+ (void)showStatus:(NSString *)status andAutoHideAfterTime:(CGFloat)showTim e;
/** 提示成功 显示默认的图片,不会自动隐藏*/
+ (void)showSuccess;
/** 提示成功 显示默认的图片,会自动隐藏*/
+ (void)showSuccessAndAutoHideAfterTime:(CGFloat)showTime;
/** 提示成功 显示默认的图片,同时显示设定的文字提示,不会自动隐藏*/
+ (void)showSuccessWithStatus:(NSString *)status;
/** 提示成功 显示默认的图片,同时显示设定的文字提示,会自动隐藏*/
+ (void)showSuccessWithStatus:(NSString *)status andAutoHideAfterTime:(CGFlo at)showTime;
/** 提示失败 显示默认的图片,不会自动隐藏*/
+ (void)showError;
/** 提示失败 显示默认的图片,会自动隐藏*/
+ (void)showErrorAndAutoHideAfterTime:(CGFloat)showTime;
/** 提示失败 显示默认的图片,同时显示设定的文字提示,不会自动隐藏*/
+ (void)showErrorWithStatus:(NSString *)status;
/** 提示失败 显示默认的图片,同时显示设定的文字提示,会自动隐藏*/
+ (void)showErrorWithStatus:(NSString *)status andAutoHideAfterTime:(CGFloa t)showTime;
/** 提示正在加载 显示默认的图片 不会自动隐藏*/
+ (void)showProgress;
/** 提示正在加载 显示默认的图片,同时显示设定的文字提示 不会自动隐藏*/ + (void)showProgressWithStatus:(NSString *)status;
/** 弹出自定义的提示框 不会自动隐藏*/
+ (void)showCustomHUD:(UIView *)hudView;
/** 弹出自定义的提示框 会自动隐藏*/
+ (void)showCustomHUD:(UIView *)hudView andAutoHideAfterTime:(CGFloat)showTi me;
/** 移除提示框*/
+ (void)hideHUD;
/** 移除所有提示框*/
+ (void)hideAllHUDs; @end

###构思和实现部分 因为我们希望是在APP运行的时候,在任何页面都能够方便的弹出HUD来,所以我们使用一个view来作为HUD的容器,在需要的地方直接弹出这个view即可,弹出的方式有两种

  • 添加为当前的view的subview展示到页面上
  • 直接使用一个window来弹出HUD,以便于实现弹出的HUD在页面所有的控件的最上面

上面提到的两种方式,我们采用第二种,因为window本身就是一个单例对象,并且很方便的在各个地方获取到,而且可以保证它在所有控件的最上层

接着看我们的层次关系,弹出的分为两层,底层是TCProgressHUD这个view层,用来作为hudView的容器,第二层就是hudView,用来显示弹出的内容 Alt text 首先我们创建TCProgressHUD,这个view作为容器,是被window弹出来的,所以必然对外提供接口,初始化方法,被window弹出来的和移除的方法

// 初始化方法
- (instancetype)init;
// 设置/添加hudView的方法
- (void)setHudView:(UIView *)hudView;
// 被window弹出的方法,同时设置显示的时间,当设置的时间小于等于0的时候将不会自动移除 
- (void)showWithTime:(CGFloat)time;
// 移除hud的方法
- (void)hide;
// 移除所有hud的方法
- (void)hideAllHUDs;

接下来就是这些方法的内部实现了,这里就不一一赘述了,无非就是添加到window上,移除子控件等操作。但是有个地方需要注意,一般的HUD都会在几秒后消失这种需求,所以我们在这里规定一下API,传入时间为0的时候,默认不会消失,传入时间大于0,那么就按传入时间来倒计时:

- (void)showWithTime:(CGFloat)time {
 UIWindow *window = [[UIApplication sharedApplication] keyWindow];
 if (self.superview == nil) { 
	 [window addSubview:self];
 }
 if (time > 0) {
 __weak typeof(self) weakself = self;
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 __strong typeof(weakself) strongSelf = weakself;
 if (strongSelf) {
 [strongSelf hide];
 } });
 }
}

那么我们在使用的时候就可以像下面的方式使用了:

 /// 初始化contentView
 TCProgressHUD *contentView = [[TCProgressHUD alloc] init];
 contentView.frame = CGRectMake(100.0f, 100.0f, 200.0f, 200.0f); // 居中显示
 contentView.center = self.view.center;
 /// 初始化HUDView 和contentView大小一样
 UILabel *hudView = [[UILabel alloc] initWithFrame:contentView.bounds];
 hudView.textColor = [UIColor redColor];
 hudView.text = @"提示文字...";
 /// 设置/添加HUDView
 [contentView setHudView:hudView];
 // 被window弹出, 同时设置显示的时间, 当设置的时间小于等于0的时候将不会自动移除
 // 这里设置为0 则会一直显示在页面上, 直到手动调用hide方法才会被移除
 [contentView showWithTime:0];
 /// 这里模拟延时操作
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
 [contentView hide];
 });

现在就完成了自定义弹窗了,而且自定义程度非常高。。。但是这样的东西只能自己用用,因为使用太复杂了,我们需要初始化几个对象,设置frame,调用好几个方法,每次这样操作自己都觉得麻烦,所以,接下来就是怎样把自己写的效果,封装一下,达到好用方便的第三方效果

封装的时候,首先第一要想的就是别人想要怎么使用,怎么使用起来方便,并且合乎开发狗的逻辑思维。除了自定义一些视图,是不是有一些经常固定的几种视图可以封装起来。像我们的这个HUD,首先考虑到是常用的功能,提示框一般居中显示,提示文字,提供成功失败的图片和文字,提供进度的动画等等

比如:提示文字的需求,我们就可以封装一下,我希望使用的接口是这样的:

// 弹出文字提示框, 提示框居中显示, 宽度会随着文字的长度调整 
[TCProgressHUD showStatus:@"正在努力加载中..."];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SE C)), dispatch_get_main_queue(), ^{
// 加载完后 移除提示框
 [TCProgressHUD hideHUD];
});

接着实现这两个类方法,实现还是很简单的,和我们之前直接使用的方法类似,只需要做一些调用即可:

/// 获得单例对象
TCProgressHUD *hudView = [TCProgressHUD sharedInstance];
/// 这里讲前面的UILabel换成了TCTextOnlyHUDView来显示文字
/// 所以需要封装一个TCTextOnlyHUDView来显示文字提示,并且在其中处理根据文字长的来设置frame的过程 所以在这里我们并没有设置frame
TCTextOnlyHUDView *textView = [[TCTextOnlyHUDView alloc] init];
textView.text = status;
/// 设置/添加 textView
[hudView setHudView:textView];
///弹出textView 设置时间为0.0f, 表示不需要自动被移除 
[hudView showWithTime:0.0f];

当然每次都需要模拟一下延迟调用移除HUD太麻烦了,所以我们也可以这样调用

// 传入延迟1S时间
[TCProgressHUD showStatus:@"正在努力加载中..." andAutoHideAfterTime:1.0];

这里有个地方需要注意:在showStatus的方法里面,我们都没有设置它的frame,其实在这里是可以直接设置的,就是计算文字的尺寸,然后设置两者的frame,但是我们并不应该在这里设置。因为我们可能APP会有横屏的时候,如果在这里设置还要去监听旋转的通知,再重新设置frame,所以在这里将两者的frame设置放到了他们的layoutSubviews方法里面,这样就可以自动适配了

所以在TCProgressHUD文件里面,重写layoutSubviews方法

- (void)layoutSubviews {
 [super layoutSubviews];
 if (self.superview) {// superView == window
 self.frame = self.superview.bounds;
 for (UIView *subview in self.subviews) {
	 // 这里的判断其实是封装完全之后,为了区分自定义HUD还是常规的几种
 if ([subview conformsToProtocol:@protocol(TCPrivateHUDProtocol)]) {
 /// 居中显示
 subview.center = self.center;
 }
 else {
 /// 自定义的hudView
 CGRect frame = subview.frame;
 subview.frame = frame;
 
 }
 }
 }
}

然后还需要新建继承自UIView的TCTextOnlyHUDView文件,这里只需要有一个Label来显示文字就好,同时还需要有一个对外的属性text来接受外部的文字,然后根据文字计算frame:

@interface TCTextOnlyHUDView : UIView
/** 设置提示文字*/
@property (strong, nonatomic) NSString *text; 
@property (strong, nonatomic) UILabel *label;
@end

具体的实现就不写出来了,这里就说一下计算frame的部分,重写text的set方法

- (void)setText:(NSString *)text {
 self.label.text = text;
 const CGFloat padding = 10.0f;
 CGRect textRect = [text boundingRectWithSize:CGSizeMake(MAXFLOAT, 100.0f) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: self.label.font} context:nil];
 CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
 CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
 CGRect frame = self.frame;
 frame.size.width = textRect.size.width + 2*padding;
 if (frame.size.width > screenWidth) {
 frame.size.width = screenWidth - 2*padding;
 }
 frame.origin.x = (screenWidth - frame.size.width)/2;
 frame.size.height = textRect.size.height + 2*padding;
 frame.origin.y = (screenHeight - frame.size.height)/2;
 self.frame = frame;
 [self layoutIfNeeded];
}

到目前为止,我们就实现了文字提示的接口,接下来一些其他的常用的接口就都可以按照这个设计规范来实现了,比如:成功失败的文字提示,成功失败的图片提示,加载中的转圈提示等等,在最终的demo里面已经实现了这些功能,运行的时候就可以看到效果和查看源码


在写这个小工具之前,我也看了MBProgressHUD的源码,打开一看,就一个.m文件,瞬间很惊讶,难道是我实现这些功能太复杂了?我用了好多.m文件来着,进入里面一看,1000多行的代码,而且里面定义了一大堆宏,到处都有宏判断,瞬间觉得智商不够用,不知道从什么地方开始读......

自习看了下MBPregressHUD的源码,其实里面由很多class文件组成,这样一看就和我们之前的设计差不多了 Alt text

按照刚刚我们自己写的HUD来看,MBProgressHUD这个class应该是作为容器的,那么里面应该类似的处理了弹出,移除,设置frame等操作,其他的class应该就是所谓的自定义view的部分了,那么就重点看看MBProgressHUD这个class里面的东西

看一下他的使用demo:

- (void)colorExample {
	MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
	hud.contentColor = [UIColor colorWithRed:0.f green:0.6f blue:0.7f alpha:1.f];
	// Set the label text.
	hud.label.text = NSLocalizedString(@"Loading...",@"HUD loading title"); 
	dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
^{
	[self doSomeWork];
	dispatch_async(dispatch_get_main_queue(), ^{ 
		[hud hideAnimated:YES];
	}); });
}

第一眼看上去觉得和我们的API有很大的区别,不过仔细看看,极其的类似。只是MBPregressHUD这个自定义的程度更高。虽然都是提供了类方法,里面处理和很多初始化的操作,不过MB并没有采用单例模式,所以在每个弹出hud的地方都会重新初始化(这里的内存优劣就不作研究了),他这样做就便于了每个地方的hud的一些属性的自定义。这里人家的MBPregressHUD的确牛逼,但是我们自己写的HUD也可以根据实际也去需求来拓展,而且可以修改自己的HUD,不需要去修改别人的三方,还担心代码不兼容。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /