基于MBProgressHUD的封装

1、前言

ios开发中,最经典也是最常用的提示框就是MBProgressHUD了,用于在执行一些任务时的提示效果,但它也有一定的弊端,就是封装的不是很好,每次我们使用的时候就要费很大的劲写一堆重复的代码,这对于我们天生有着强迫症的程序猿来说是绝对不能容忍的,更让人抓狂的是,去后台请求数据时,如果数据量比较大,网速又很low,那么那个loading就一直在那转个不停,点了半天没有反应,想取消又取消不掉,瞬间砸手机的冲动都有了!对于有着代码洁癖的我来说又不想在每次请求数据的时候都去写一堆判断什么时候该去创建和取消提示框,于是顺手写了这篇文章。创建MBManager类,只需一句代码就能搞定搞定提示框的调用,另外我在里面添加了手势功能,只要点击屏幕loading就再也不见了,瞬间心情舒畅了很多,有木有!当然如果你比较任性或者由于某种需求不需要这个手势,那么直接在.m里面进行修改配置就可以了。

  • 特性
    1、对MBProgressHUD就行封装,调用只需要一句代码
    2、添加手势功能,触摸屏幕就可以取消提示框
    3、多种效果可供选择
    4、demo在项目中使用,发现bug后立马更新
  • 效果
MBManager.gif

更新说明:2017.04.09
很久没有更新了,这次改动有点大,保持之前的调用方法不变,与之前的版本兼容,但效果的实现方式与之前的就不一样了,所以文章下面对实现的讲解可以略过了,因为实现完全不一样了,事例界面也做了改变,使用方法更加全面,可以及时看到效果,这里对实现部分不再赘述,具体可以参看demo,需要说明的是如果你在使用工程中遇到这样的一个问题:在app刚启动的时候调用如[MBManager showLoading]可能会发现提示框并没有出来,原因就是这种方式是把提示框加载到当前window上,而window此时还没有创建成功,所以就出现了这个情况,目前的解决方案是用[MBManager showLoadingInView:self.view]替代,如果发现更好的方案会再进行修改。
如下:


事例界面.png

再贴一下.h文件吧:

/**
 *  是否显示变淡效果,默认为YES,  PS:只为 showPermanentAlert:(NSString *) alert 和 showLoading 方法添加
 */
+ (void)showGloomy:(BOOL) isShow;
/**
 *  显示“加载中”,带圈圈,若要修改直接修改kLoadingMessage的值即可
 */
+ (void)showLoading;
/**
 *  自定义等待提示语,效果同showLoading
 *
 *  @param title 提示语
 */
+ (void)showWaitingWithTitle:(NSString *)title;
/**
 *  一直显示自定义提示语,不带圈圈
 *
 *  @param alert 提示信息
 */
+ (void) showPermanentAlert:(NSString *) alert;
/**
 *  显示简短的提示语,默认2秒钟,时间可直接修改kShowTime
 *
 *  @param alert 提示信息
 */
+ (void) showBriefAlert:(NSString *) alert;
/**
 自定义加载视图
 @param imageName 图片名字
 @param title 标题
 */
+(void)showAlertWithCustomImage:(NSString *)imageName title:(NSString *)title;
/**
 *  隐藏alert
 */
+(void)hideAlert;


/****************************************************
 *                                                                             *
 *                以下方法是为指定的view添加提示                *
 *                                                                             *
 *****************************************************
 */

/**
 *  显示简短提示语到view上
 *
 *  @param message 提示语
 *  @param view    要添加到的view
 */
+ (void) showBriefAlert:(NSString *) message inView:(UIView *) view;
/**
 *  显示长久的(只要不用手触摸屏幕或者调用hideAlert方法就会一直显示)提示语到view上
 *
 *  @param message 提示语
 *  @param view    要添加到的view
 */
+ (void) showPermanentMessage:(NSString *)message inView:(UIView *) view;
/**
 *  显示网络加载到view上
 *
 *  @param view 要添加到的view
 */
+ (void) showLoadingInView:(UIView *) view;

/**
 自定义加载提示语(效果同showLoading)

 @param title 提示语
 @param view 要添加到的view
 */
+ (void)showWaitingWithTitle:(NSString *)title inView:(UIView *)view;
/**
 *  自定义加载视图接口,支持自定义图片
 *
 *  @param imageName  要显示的图片,最好是37 x 37大小的图片
 *  @param title 要显示的提示文字
 *  @param view 要把提示框添加到的view
 */
+(void)showAlertWithCustomImage:(NSString *)imageName title:(NSString *)title inView:(UIView *)view;

2、demo示例

大家可以先下载下来demo运行一下看看效果,和这个gif图不太一样,这个图片是我没有修改之前录制的效果,但总体是差不多的。

  • 头文件里面有这么些内容,很简单,不做过多介绍,大家看注释就行了,很清晰:
    (1)定义预设提示语和显示时间,可根据需要在这直接修改
static NSString *const kLoadingMessage = @"加载中"; //定义网络加载时显示的提示语,可以在此直接修改成想要的提示,达到定义一次,全局有一样
static CGFloat const   kShowTime  = 2.0f;//定义自动消失的提示语显示的时间,可直接修改

(2)蒙层属性,默认为YES

//是否显示变淡效果,默认为YES,  PS:只为 showPermanentAlert:(NSString *) alert 和 showLoading 方法添加
@property (nonatomic, assign) BOOL isShowGloomy;

(3)最常用的三种方法,加载提示框到当前的window上

/**
 *  显示“加载中”,待圈圈,若要修改直接修改kLoadingMessage的值即可
 */
+(void) showLoading;
/**
 *  一直显示自定义提示语,不带圈圈
 *
 *  @param alert 提示信息
 */
+(void) showPermanentAlert:(NSString *) alert;
/**
 *  显示简短的提示语,默认2秒钟,时间可直接修改kShowTime
 *
 *  @param alert 提示信息
 */
+(void) showBriefAlert:(NSString *) alert;
/**
 *  隐藏alert
 */
+(void)hideAlert;

(4)添加提示框到特定的view上,一般用不到,除非要添加提示框到特定的view上,这一点在demo里面也有演示,大家可以下载下来看一下效果。

/***************************************
 *                                     *
 *  以下方法根据情况可选择使用,一般使用不到  *
 *                                     *
 ***************************************
 */ 
/**
 *  显示简短提示语到view上
 *
 *  @param message 提示语
 *  @param view    要添加到的view
 */
+(void) showBriefMessage:(NSString *) message InView:(UIView *) view; 
/**
 *  显示长久的(只要不用手触摸屏幕或者调用hideAlert方法就会一直显示)提示语到view上
 *
 *  @param message 提示语
 *  @param view    要添加到的view
 */
+(void) showPermanentMessage:(NSString *)message InView:(UIView *) view; 
/**
 *  显示网络加载到view上
 *
 *  @param view 要添加到的view
 */
+(void) showLoadingInView:(UIView *) view;

3、demo详解

(1)创建单例
保证整个工程只有一个MBManager实例

//用GCD来创建单例
+(instancetype )shareManager{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        hudManager = [[self alloc] init];
    });
    return hudManager;
}

(2)初始化,重写init方法

-(instancetype)init{
    if (self = [super init]) {
        [self initBackView];//创建窗口蒙层,出现北京变淡的效果
        self.isShowGloomy = YES;//默认打开蒙层,如果不想要这个效果,可以直接修改为NO
    }
    return self;
}

(3)创建蒙层

-(void)initBackView{
    //创建蒙层,默认初始大小为整个屏幕,设置背景色为黑色,透明度为0.5,隐藏属性为YES,当需要的时候再设其为NO
    bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreen_width, kScreen_height)];
    bottomView.backgroundColor = [UIColor blackColor];
    bottomView.alpha = 0.5;
    bottomView.hidden = YES;
}

(4)停留的提示框

//调用此方法产生的提示框会长时间的停留在界面,直到用手触摸屏幕或者调用hideAlert方法
+ (void) showPermanentMessage:(NSString *)message InView:(UIView *) view{
    hudAddedView = view;//记录当前提示框添加到的view
    [self shareManager];//创建Manager
    if (view == nil) {
        view = [[UIApplication sharedApplication] windows].lastObject;//如果view为nil,则把当前的window赋值为view,把提示框添加到当前的window上
    }
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];//创建hud
    hud.labelText = message;//要显示的提示语
    hud.removeFromSuperViewOnHide = YES;//hud隐藏时同时从父视图上移除
    hud.mode = MBProgressHUDModeCustomView;//设置hud的样式
    if (hudManager.isShowGloomy) {//根据isShowGloomy的值来判断要不要显示蒙层效果
        //如果添加了view则将botomView的frame修改与view一样大
        if (hudAddedView) {//如果设置了要把提示框添加到的view则把蒙层的frame设置成和要添加的view的frame一样大
            bottomView.frame = CGRectMake(0, 0, hudAddedView.frame.size.width, hudAddedView.frame.size.height);
        }
        [view addSubview:bottomView];//添加蒙层到view上
        [hudManager showBackView];//此时蒙层并没有显示,因为botomView的hide属性为YES,需调用此方法蒙层效果才会出现
    }
    [view bringSubviewToFront:hud];//把hud移到最上面,否则hud的效果有点淡,因为被botomView遮挡了
    [hudManager addGestureInView:view];//添加手势
}

(5)网络加载用,统一显示预设的提示
显示转圈圈,一般用于网络请求数据或者处理较大量的数据时用

+ (void) showLoadingInView:(UIView *) view{
    hudAddedView = view;
    [self shareManager];
    if (view == nil) {
        view = [[UIApplication sharedApplication] windows].lastObject;
    }
    MBProgressHUD *hud = [[MBProgressHUD alloc] initWithView:view];
    hud.labelText = kLoadingMessage;//预设的提示语,直接修改其值
    hud.removeFromSuperViewOnHide = YES;
    if (hudManager.isShowGloomy) {
        //如果添加了view则将botomView的frame修改与view一样大
        if (hudAddedView) {
            bottomView.frame = CGRectMake(0, 0, hudAddedView.frame.size.width, hudAddedView.frame.size.height);
        }
        [view addSubview:bottomView];
        [hudManager showBackView];
    }
    [view addSubview:hud];
    [hud show:YES];//调用此方法后才会显示
    [hudManager addGestureInView:view];
}

(6)显示简短、简捷的提示语
这种效果就是很窄的那种一句话提示,默认2秒后自动消失,当然也是支持手势的,具体效果可以直接看demo

+ (void) showBriefMessage:(NSString *) message InView:(UIView *) view{
    hudAddedView = view;
    [self shareManager];
    if (view == nil) {
        view = [[UIApplication sharedApplication] windows].lastObject;
    }
    MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES];
    hud.labelText = message;
    hud.mode = MBProgressHUDModeText;
    hud.margin = 10.f;
    //HUD.yOffset = 200;
    hud.removeFromSuperViewOnHide = YES;
    [hud hide:YES afterDelay:kShowTime];//要显示的时间可以在这修改
    [hudManager addGestureInView:view];
}

(7)最常用的三种方法
上面那几个方法很少用的,最常用的就是下面这三个,不需要传要添加的view,只需要输入我们要显示的提示语就可以了,甚至于连提示语都不需要输入,想想都很酷的样子

//很简单,不再作过多介绍,只需要调上面对应的方法,传参为nil就可以了
+(void)showLoading{
    [self showLoadingInView:nil];
}
+(void)showBriefAlert:(NSString *)alert{
    [self showBriefMessage:alert InView:nil];
}
+(void)showPermanentAlert:(NSString *)alert{
    [self showPermanentMessage:alert InView:nil];
}

(8)有提示就得有隐藏

+(void)hideAlert{
    [hudManager hideBackView];
    UIView *view ;//找到提示框添加到的view
    if (hudAddedView) {
        view = hudAddedView;
    }else{
        view = [[UIApplication sharedApplication].windows lastObject];
    }
    [self hideHUDForView:view];//将hud从view上移除
}
+ (void)hideHUDForView:(UIView *)view
{
    [self hideHUDForView:view animated:YES];
}
+ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated {
    MBProgressHUD *hud = [self HUDForView:view];
    if (hud != nil) {
        hud.removeFromSuperViewOnHide = YES;
        [hud hide:animated];
        return YES;
    }
    return NO;
}
+ (MBProgressHUD *)HUDForView:(UIView *)view {
    NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
    for (UIView *subview in subviewsEnum) {
        if ([subview isKindOfClass:[MBProgressHUD class]]) {
            return (MBProgressHUD *)subview;
        }
    }
    return nil;
}

(9)有关蒙层的两个方法

//也很简单,就是控制hidden属性和frame,以及手势的移除
-(void)showBackView{
    bottomView.hidden = NO;
}
-(void)hideBackView{
    bottomView.hidden = YES;
    [tap removeTarget:nil action:nil];
    bottomView.frame = CGRectMake(0, 0, kScreen_width, kScreen_height);
}

(10)点击屏幕

//去除蒙层,移除手势,隐藏hud
-(void)tapTheScreen{
    NSLog(@"点击屏幕");
    [hudManager hideBackView];
    [tap removeTarget:nil action:nil];
    [MBManager hideAlert];
}

(11)解决手势冲突

//主要解决手势与button和tableView的冲突,在项目中有出现了与自定义的segment冲突,导致segment点击无效,于是添加此方法予以解决
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([NSStringFromClass([touch.view class]) isEqualToString:@"PKProductMainListTableViewCellContentView"]) {
        return NO;
    }
    if ([touch.view isKindOfClass:[UITableViewCell class]]) {
        return NO;
    }
    if ([touch.view isKindOfClass:[UIButton class]]) {
        return NO;
    }
    return YES;
}
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    return YES;
}

4、使用

使用方法很简单,只需要导入MBManager和MBProgressHUD库(如果你的项目里面已经包含MBProgressHUD不要重复导入),然后在需要的地方import MBManager,然后就可直接调用了,建议在pch里面直接导入MBManager,这样在全工程都可以用了:

- (IBAction)showTextOnly:(UIButton *)sender {
    [MBManager showBriefMessage:@"提示语" InView:self.view];
}
- (IBAction)showStill:(UIButton *)sender {
#if 0
    [MBManager showPermanentAlert:@"一直显示"];
#else
    [MBManager showPermanentMessage:@"一直显示" InView:self.backView];
#endif
}

- (IBAction)showDelay:(UIButton *)sender {
#if 1
    [MBManager showLoading];
#else
    [MBManager hideAlert];
    [MBManager showBriefAlert:@"hello"];
#endif
}

总结

我本人也是刚开始在简书上写些东西,以供记录自己学习的点滴,同时也希望能给大家提供点便利,不奢求star,但毕竟也写了一上午才弄好,如果好用还请大家多多支持,不足之处,欢迎指点!
另附下载链接:
MBManager下载地址


  • 更新:
    发现一个bug,就是调用提示框的方法后在textView的界面会出现偶尔不能输入任何字符的问题,目前可以做的是将
self.isShowGloomy = YES;

修改为

self.isShowGloomy = NO;

后续问题会加快解决。


  • 问题解决
    这个问题着实让我费了一番功夫,说出来也很简单,但就是有的时候偏偏就犯了,以致于“害人害己”,或许是坑遇到的还不太多,继续加油,争取少犯错。原因就在于我在hideAlert时将手势的移除操作这样来做了:
[tap removeTarget:nil action:nil];

这样写有什么问题呢?问题就在于这样就移除了所有的tap手势,而这个tap手势是加到当前的window上的,那么移除了之后就把系统的一些tap手势也移除掉了,然后就造成了textView输入时无论如何也显示不出到屏幕上,解决方法就是把这句代码直接去掉就行
代码已更新,大家直接下载就行。

  • 更新:2016.04.06
    增加接口
+(void)showAlertWithCustomImage:(NSString *)imageName title:(NSString *)title inView:(UIView *)view;

支持自定义图片的alert。


  • 更新 2016.04.12
    呵呵呵呵呵......又来更新了,说这话有点不地道,不过我确实又发现了问题,之前的手势冲突几经修改,仍然有问题,包括我上面的更新中说的解决方法估计是错的,当时确实起作用了,而现实可能是xcode当时抽疯了,现在又不行了,于是我又开始了漫长的找bug之旅,经过一个小时的奋战,我发现我竟然连手势的协议都木有遵守,那方法又怎么可能会调用呢?!刚开始协议我是遵守了的,可能是在找bug的过程中我也跟着抽疯了,不知为何竟把遵守的协议给删了!!所以现在我又回来了,遵守了协议,又是费了半天的功夫替换了判断手势冲突的方法:
#pragma mark - 解决手势冲突
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    if ([touch.view isKindOfClass:[MBProgressHUD class]]) {
        return YES;
    }else{
        return NO;
    }
}

而这一次我认为应该不会再出现手势方面的问题了,因为这样写就已经保证了此手势只会在MBPRogressHUD上起作用,其他任何地方该手势都被禁用,这样还会出现什么问题呢?我是暂时没想到,也没有遇到。


  • End
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • 这里有源码分析:http://www.jianshu.com/p/485b8d75ccd4 源码笔记---MBPr...
    晓飞90阅读 769评论 0 2
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,614评论 4 59
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 我们 经常提到智慧一词,可是什么是智慧呢?我真的说不上来。只是觉得智慧不是知识,智慧不是智力,智慧也不是...
    清心阁阅读 204评论 0 1
  • 今天忙工作,一天都没闲着。也许因为昨天头疼刚好,今天有点疲惫。前后打了几个盹。竟然忘了今天是周二,所以也没去看电影...
    小王加油啊阅读 241评论 0 0