MBProgressHUD是一个显示提示窗口的三方库,常用于用户交互、后台耗时操作等的提示。通过显示一个提示框,通知用户操作或任务的执行状态;同时,利用动画效果,降低用户等待的焦虑心理,增强用户体验。

本篇文章从源码角度来看一下MBProgressHUD是如何实现的,所用的知识都是比较基础的,不过还是值得我们学习一下。详解如下:

1. 类介绍

  • MBProgressHUD

    这是MBProgressHUD的主要类,提供丰富的属性来调整视图的样式。
  • MBRoundProgressView

    这是提供Determinate视图显示的类,有非圆环和圆环视图两种方式。
  • MBBarProgressView

    这是提供进度条的视图类。
  • MBBackgroundView

    这是MBProgressHUD的背景视图类,利用UIVisualEffectView提供毛玻璃效果

2. MBProgressHUD类的显示模式

  • MBProgressHUDModeIndeterminate

![Indeterminate](//upload-images.jianshu.io/upload_images/1843940-7d2ced265a958ace.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • MBProgressHUDModeDeterminate

![Determinate](//upload-images.jianshu.io/upload_images/1843940-0729bfea6d1ffb75.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • MBProgressHUDModeDeterminateHorizontalBar

![DeterminateHorizontalBar](//upload-images.jianshu.io/upload_images/1843940-24816377417a2ade.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • MBProgressHUDModeAnnularDeterminate

![AnnularDeterminate](//upload-images.jianshu.io/upload_images/1843940-37b8afa115eb5fe8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • MBProgressHUDModeCustomView

这是自定义视图

  • MBProgressHUDModeText

![Text](//upload-images.jianshu.io/upload_images/1843940-ca92d0d489e9c287.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

3.动画模式

  • MBProgressHUDAnimationFade : 渐变模式
  • MBProgressHUDAnimationZoom : Zoom In & Zoom Out
  • MBProgressHUDAnimationZoomOut : 消失时带变小动画
  • MBProgressHUDAnimationZoomIn : 出现时带变大动画

4. 背景样式

  • MBProgressHUDBackgroundStyleSolidColor : 正常颜色
  • MBProgressHUDBackgroundStyleBlur : 毛玻璃效果

5. 视图内容

  • @property (strong, nonatomic, readonly) UILabel *label; : 标题
  • @property (strong, nonatomic, readonly) UILabel *detailsLabel; :详情
  • @property (strong, nonatomic, readonly) UIButton *button : 按钮(显示在标题下方)
  • @property (strong, nonatomic, nullable) UIView *customView; :用户自定义视图
  • @property (strong, nonatomic, readonly) MBBackgroundView *backgroundView; : 整个背景视图
  • @property (strong, nonatomic, readonly) MBBackgroundView *bezelView; :提示框背景视图
  • @property (strong, nonatomic, nullable) UIColor *contentColor UI_APPEARANCE_SELECTOR; : 提示框的内容颜色
  • @property (assign, nonatomic) CGPoint offset UI_APPEARANCE_SELECTOR; :提示框相对父视图中心点的偏移量
  • @property (assign, nonatomic) CGFloat margin UI_APPEARANCE_SELECTOR; :提示框内的内容视图的边距
  • @property (assign, nonatomic) CGSize minSize UI_APPEARANCE_SELECTOR; :提示框最小尺寸
  • @property (assign, nonatomic) BOOL removeFromSuperViewOnHide; :隐藏时从父视图中删除
  • @property (assign, nonatomic) NSTimeInterval graceTime; :延迟多久后显示提示框,避免快速执行的任务也显示提示框,给用户造成视觉干扰。
  • @property (assign, nonatomic) NSTimeInterval minShowTime; :提示框视图最少展示的时间

6. 创建和隐藏视图

  • 创建流程

    通过 + (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated 类方法创建视图,也可以通过对象方法创建,不过建议用类方法,不仅创建方便,而且会自动的添加到父视图,然后进行显示。其中,创建过程如下:
- (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]; [self updateIndicators];
[self registerForNotifications];
}

我们可以发现,通过添加子空间后,根据视图模式调用updateIndicators 方法来创建不同的视图,最后添加了一个状态栏的通知,用来在横竖屏时跳转视图。其中,在显示提示框时,会首先判断graceTime,如过不为0,那么就创建一个定时器倒计时,时间到之后再判断任务是否结束,如果finished 不为空,就开始显示提示框。

- (void)showAnimated:(BOOL)animated {
MBMainThreadAssert();
[self.minShowTimer invalidate];
self.useAnimation = animated;
self.finished = NO;
// If the grace time is set, postpone the HUD display
if (self.graceTime > 0.0) {
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 immediately
else {
[self showUsingAnimation:self.useAnimation];
}
}
- (void)handleGraceTimer:(NSTimer *)theTimer
{
// Show the HUD only if the task is still running
if (!self.hasFinished) {
[self showUsingAnimation:self.useAnimation];
}
}
  • 隐藏视图 通过 + (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated 隐藏视图,其中会根据minShowTime来判断是否立即隐藏提示框。如果,minShowTime 不为0,那么会创建一个定时器,并把定时器加入到common模式的runloop里,等时间到后再把提示框隐藏。
- (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 postpone the hiding operation if necessary
if (self.minShowTime > 0.0 && self.showStarted) {
NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted];
if (interv < self.minShowTime) {
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];
}

7. MBRoundProgressView 和 MBBarProgressView

这两个类,分别创建了 Determinate 和 进度条 的提示框视图,具体实现方法是在 - (void)drawRect:(CGRect)rect 方法里通过 UIBezierPath 或者 Quarts2D 画出,设计思想算是常规,请参考代码细读。

8. MBProgressHUD应用

对于三方框架,使用之前,最好先封装一层(继承或分类),方便以后的调试和新框架替换。封装时,尽量用类方法,使用时比较简洁。

  • 添加提示框
+ (void)showHUDWithText:(NSString *)text inView:(UIView *)view deley:(NSTimeInterval)time
{
if (text == nil || text.length <= 0) {
return;
} if (view == nil) {
view = [[UIApplication sharedApplication].windows lastObject];
} MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:view animated:YES];
HUD.mode = MBProgressHUDModeText; [HUD hideAnimated:YES afterDelay:1.5];
}
  • 隐藏提示框 (改方法调用时,最好在主线程,异步线程可能会出现问题)
+ (void)hideHUDForView:(UIView *)view
{
if (view == nil) view = [[UIApplication sharedApplication].windows lastObject];
[self hideHUDForView:view animated:YES];
}

参考资料

https://github.com/jdg/MBProgressHUD

MBProgressHUD1.0.0源码解析的更多相关文章

  1. Masonry1.0.2 源码解析

    在了解Masonry框架之前,有必要先了解一下自动布局的概念.在iOS6之前,UI布局的方式是通过frame属性和Autoresizing来完成的,而在iOS6之后,苹果公司推出了AutoLayout ...

  2. SpringBoot 2.0.3 源码解析

    前言 用SpringBoot也有很长一段时间了,一直是底层使用者,没有研究过其到底是怎么运行的,借此机会今天试着将源码读一下,在此记录...我这里使用的SpringBoot 版本是  2.0.3.RE ...

  3. YYModel V1.0.4源码解析

    YYKit出现了很长时间了,一直想要详细解析一下它的源码,都是各种缘由推迟了. 最近稍微闲了一点,决定先从最简单的YYModel开始吧. 首先,我也先去搜索了一下YYModel相关的文章,解析主要AP ...

  4. 【JUC源码解析】CyclicBarrier

    简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...

  5. Redis系列(十):数据结构Set源码解析和SADD、SINTER、SDIFF、SUNION、SPOP命令

    1.介绍 Hash是以K->V形式存储,而Set则是K存储,空间节省了很多 Redis中Set是String类型的无序集合:集合成员是唯一的. 这就意味着集合中不能出现重复的数据.可根据应用场景 ...

  6. ArrayList、CopyOnWriteArrayList源码解析(JDK1.8)

    本篇文章主要是学习后的知识记录,存在不足,或许不够深入,还请谅解. 目录 ArrayList源码解析 ArrayList中的变量 ArrayList构造函数 ArrayList中的add方法 Arra ...

  7. EventBus3.0源码解析

    本文主要介绍EventBus3.0的源码 EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递. EventBus使用简单,并将事件发布和订阅充 ...

  8. solr&lucene3.6.0源码解析(四)

    本文要描述的是solr的查询插件,该查询插件目的用于生成Lucene的查询Query,类似于查询条件表达式,与solr查询插件相关UML类图如下: 如果我们强行将上面的类图纳入某种设计模式语言的话,本 ...

  9. solr&lucene3.6.0源码解析(三)

    solr索引操作(包括新增 更新 删除 提交 合并等)相关UML图如下 从上面的类图我们可以发现,其中体现了工厂方法模式及责任链模式的运用 UpdateRequestProcessor相当于责任链模式 ...

  10. Heritrix 3.1.0 源码解析(三十七)

    今天有兴趣重新看了一下heritrix3.1.0系统里面的线程池源码,heritrix系统没有采用java的cocurrency包里面的并发框架,而是采用了线程组ThreadGroup类来实现线程池的 ...

随机推荐

  1. zookeeper初试

    实验环境: os-platform: windows7 x64 jdk: 1.7 参考文档: http://www.ibm.com/developerworks/cn/opensource/os-cn ...

  2. jsPlumb之流程图项目总结及实例

    在使用jsPlumb过程中,所遇到的问题,以及解决方案,文中引用了<数据结构与算法JavaScript描述>的相关图片和一部分代码.截图是有点多,有时比较懒,没有太多的时间去详细的编辑. ...

  3. 阿里云服务器 通过JavaMail发送邮箱STMP问题( 25端口被禁用 使用SSL协议465端口 )

    我们传统使用的比较简单的是 STMP 25端口收发邮件 今天发现刚购买的阿里云服务器不能作为客户端通过STMP 25端口发送邮件 开始在网上有说发现是JDK1.8的原因,然后自己也把JDK1.8换到了 ...

  4. 架构之路 之 Nginx实现负载均衡

    [前言] 在大型网站中,负载均衡是有想当必要的.尤其是在同一时间访问量比较大的大型网站,例如网上商城,新闻等CMS系统,为了减轻单个服务器的处理压力,我们引进了负载均衡这一个概念,将一个服务器的压力分 ...

  5. Spring 自定义注解,配置简单日志注解

    java在jdk1.5中引入了注解,spring框架也正好把java注解发挥得淋漓尽致. 下面会讲解Spring中自定义注解的简单流程,其中会涉及到spring框架中的AOP(面向切面编程)相关概念. ...

  6. git的一些基本命令

    1.创建一个新的仓库:(选择一个合适的地方,创建一个空目录) $mkdir learngit //learngit是用git新建的一个目录 $cd learngit $pwd //pwd命令用于显示当 ...

  7. 使用ajax方法实现form表单的提交(附源码)

    写在前面的话 在使用form表单的时候,一旦点击提交触发submit事件,一般会使得页面跳转,页面间的跳转等行为的控制权往往在后端,后端会控制页面的跳转及数据传递,但是在某些时候不希望页面跳转,或者说 ...

  8. Easyui+MVC+FullCalendar插件实现日程记录功能

    好久好久好久,,,没有写博客了,,久到账号都忘记了....分享一个干货.... 废话少说,先看看效果图. 要实现这样一个功能,先创建一个用于存储日程的记录表(不要问我为什么都是大写,因为初版在orac ...

  9. Ambari安装之部署单节点集群

    前期博客 大数据领域两大最主流集群管理工具Ambari和Cloudera Manger Ambari架构原理 Ambari安装之Ambari安装前准备(CentOS6.5)(一) Ambari安装之部 ...

  10. 玩玩微信公众号Java版之四:自定义公众号菜单

    序: 微信公众号基本的菜单很难满足个性化及多功能的实现,那么微信能否实现自定菜单呢,具体的功能又如何去实现么?下面就来学习一下微信自定义公众号菜单吧! 自定义菜单接口可实现多种类型按钮,如下: 1.c ...