基于MBProgressHUD的封装

作者: MrFire_ | 来源:发表于2016-03-30 11:36 被阅读9163次

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

相关文章

网友评论

  • 片片碎:赞一个 支持支持
  • oriyum:看 view 的层级结构有两个 hub
  • XTK_iOS:当我设置了showGloomy 这个属性,点击showPermenent这个效果,弹窗会出来两次
    XTK_iOS:[MBManager showGloomy:YES];
  • XTK_iOS:怎么修改背景颜色呢
  • Mr_Lucifer:获取view上的hud
    NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator];
    请问下目的的什么?
    MrFire_:@Mr_Lucifer 就是反向获取一下,拿到最上层的view,不这样做也行,就是效率的问题
  • jooooker:楼主、我现在用你这个遇到个问题。showWaitingWithTitle loading的时候。一闪就消失了。但是我没有调用hideAlert方法。请问下这是什么原因啊?
    jooooker:楼主、如果有空烦请加一下QQ:784250584。我在每一个hide前打断点都没执行。直接就消失了。囧。谢谢
  • jooooker:感谢楼主无私奉献!
  • 小恋火:这个 我用MBProgressHUD 1.0.0会出现加载跑到上边去 怎么办? 要用和demo一样版本的MBProgressHUD吗?
    d5629547cb34:@hungryBoy 我的也是的 用1.0版本的 加载显示会出现在左上角
    MrFire_:@Lnnocentlovefir和版本关系不太大吧,跑到上面去是什么意思?最近比较忙,没来得及回复还望见谅
  • ducks:单例,显示多个hud 怎么处理
  • DecadentOrMagic:为什么在控制器的viewDidLoad里面直接调用 [MBManager showPermanentAlert:@"一直显示"]; 看不到任何效果?
    DecadentOrMagic:@简而不简书而不求 好的,谢谢
    HMZ4978:@DecadentOrMagic viewDidLoad 是视图正在加载的时候调用的方法, 如果放在这里, 也就是说, 在视图加载没有完成前就已经开始显示这个提示框了, 等视图加载完成后, 就把这个提示框给盖住了, 你可以看一下层级图.
  • 超_iOS:star了
  • Misaki_yuyi:大哥 你代码里面是0.9.1的版本 最新版是0.9.2 很早就更新了
    MrFire_:@Misaki_yuyi 0.9.1中占有这个哥属性:HUD.dimBackground = YES; 这个属性在0.9.2中不会删除的,9.2等忙完这段时间再研究
    Misaki_yuyi:@hungryBoy 0.9.2版本 想把风格变回原来的黑色半透明的风格,求怎么设置
    MrFire_:@Misaki_yuyi 是的呢,我刚才看了一下,0.9.2是去年12月份更新的,我这个是从去年9月份的项目里面拉出来处理一下得到的,最近一直在忙,还真没注意,太low了,谢谢!看来又得再写一个了 :sweat:
  • 睡后3k:是iOS
  • AFlyLi:赞
  • BluesACE:666666
    MrFire_:@HitHungryBoy why hit me? :sweat:
  • 慕云少年:不错,加油
    MrFire_:@HxLemon :pray:

本文标题:基于MBProgressHUD的封装

本文链接:https://www.haomeiwen.com/subject/qvyslttx.html