LipYoung / programing-blog

个人中文编程博客。
MIT License
3 stars 0 forks source link

自定义SVProgressHUD进度指示器 #1

Open LipYoung opened 6 years ago

LipYoung commented 6 years ago

SVProgressHUD 简介

SVProgressHUD,HUD全称为Head Up Display,直译过来就是进度指示器. 在任何一款面向用户且应用为主的APP中,总是需要通过某种方式告诉用户软件正在努力的进行者各种任务,或者直接显示某些文字信息到用户眼前.

例如用户登录时前,需要提示用户账号不能为空/验证码最少4位等消息. 在APP等待服务器返回的信息时,需要告诉用户,我们正在玩命儿的联系服务器.而不是像死机的一样卡主不动,产生很差的用户体验.

一个合适的指示器,能轻松的解决这些问题.

我简单的把指示器分为三类:

  1. 信息指示器

  2. 进度指示器

  3. 拦截指示器

信息指示器

顾名思义,就是直接将部分信息直接呈现给用户. 设计一款信息指示器虽然简单.但是,任然有许多细节是新手不容易考虑到的.创建一个HUD的实现思路

  1. 创建一个ProgressHUDView

  2. 找到当前所显示的Window

  3. 将ProgressHUDView添加到Window

  4. 移除ProgressHUDView

UIView *progressHUDView = [[UIView alloc] init];
// setup ProgressHUDView …
// setup ProgressHUDView childView ...
UIWindow *window = [UIApplication sharedApplication].keyWindow; 
[window addSubview: progressHUDView]; 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
    [progressHUDView removeFromSuperview]; 
}); 

创建一个ProgressHUDView

创建一个HUD时需要考虑内容是图片/文字/亦或者是动画等.需要考虑到文字长度,图片尺寸的不确定性,对ProgressHUDView尺寸以及显示时间的影响.

找到当前所显示的Window

这一步有很多的方式,简单的方法例如

UIWindow *window = [UIApplication sharedApplication].keyWindow;

对于移动端APP这种大多数时间只有单个window的情况下有较为安全.不过任然需要对在用户呼叫出键盘或者AlertView时对多window的情况单独处理.

将ProgressHUDView添加到Window上

将指示器添加到window上时就会稍显复杂,需要考虑同一个HUD添加到本工程中所有页面上时的位置,具体页面有无StatusBar(状态栏),有无NavigationBar(导航栏),工程是否支持横等情况.

移除ProgressHUDView

将视图移除,注意内存管理,避免出现不能释放的情况即可.也需要考虑到同时出现多个HUD的情况.

那么一个简单的 HUD 就实现完毕了吗 ? 不

在移除HUD之前,其实还有一个步奏,需要考虑到,指示期显示期间的一些问题. HUD并不是单纯的出现消失即可,它会在窗口上存在一部分时间,显示的时间可能会由实际的业务逻辑决定,例如从向服务器发送消息一直持续到服务器返回结果或者超时结束. 有的ProgressHUD在显示时,是允许用户操作界面,如果此时发生产生了更多操作,比如用户操作产生了多个Progress,或者跳转了页面,或者手机设备的方向产生了变化等情况,都需要一一处理.面对这种情况

面对这种情况就会使用到 拦截指示器

进度指示器 ProgressHUD 和普通的出现就消失的文本指示器不同,它会在当前窗口上持续存在一段时间.如果在它存在的过程中,屏蔽掉用户的操作,就可以简单的避免上述情况的发生.

进度指示器

在服务器和APP同步数据(上传图片等)的过程中,为了避免用户焦虑,更好的产品体验是使用经度指示器,将任务进行的过程详细直观的显示给用户.

那为什么选择SVProgressHUD?

一个看似简单的文本指示器实现起来还是有些耗时耗力,为了更好的用户体验,建议还是选择成熟的三方框架来解决以上问题. SVProgressHUD目前在github上获得了7,750颗星,关注度相当高,属于非常流行的iOS第三方框架.对于以上的细节也都做出了相应的处理.而且SVProgressHUD也放出了源代码,如果出现了具体的业务逻辑无法实现时,就可以相应的进行修改.

SVProgressHUD的基本用法

SVProgressHUD使用简单,针对我之前提到的三种应用场景,只需要以下代码即可:

1.信息指示器

2.进度指示器

3.拦截指示器 因为各种状态下的指示器都可能会需要拦截用户的操作 所以SVProgressHUD提供了SVProgressHUDMaskTypeBlack格式选择 只需要在生成指示器的同时指定是够需要使用Black状态即可

源码简单分析

SVProgressHUD功能足够强大,代码安全,内存占用小,可以放心的将指示器模块的功能交给他即可.但是在实际的使用过程还是可能会需要对其进行部分的修改以适应具体的环境.

HUD的组成

SVProgressHUD由三部分组成: 通过SVProgressHUDMaskType控制的遮罩层,默认状态下占据当面整个屏幕,颜色透明 承载信息显示的hudView,默认白色,正方形,出现在屏幕中间位置.显示文本信息的stringLabel/显示图片的imageView以及显示动画的indefiniteAnimatedView都是hudView的子视图.

HUD的自定义方式

SVProgressHUD默认提供一些接口供用户自定义修改,还支持通过appearance对HUD进行定义,自定义的接口在SVProgressHUD.h 文件中可以看到:

// 背景色 默认是白色
+(void)setBackgroundColor:(UIColor*)color;
// 前景色 默认是黑色
+(void)setForegroundColor:(UIColor*)color;
// 圆角角度 默认是4
+(void)setRingThickness:(CGFloat)width;
// 文本信息字体,默认使用系统 [UIFont preferredFontForTextStyle:UIFontTextStyleSubheadline]                  
+(void)setFont:(UIFont*)font;
// info信息的图片,会自动对图片渲染
+(void)setInfoImage:(UIImage*)image;                      
// success时的图片
+(void)setSuccessImage:(UIImage*)image;
// error时的图片
+(void)setErrorImage:(UIImage*)image;
// 设置默认状态下遮罩的状态 默认是SVProgressHUDMaskTypeNone状态
+(void)setDefaultMaskType:(SVProgressHUDMaskType)maskType;

修改SVProgressHUD的位置

通过offset 设置水平系数和垂直系数,整个HUD指示器会在原来居中的基础上相对的垂直移动,水平移动(正数和负数决定方向)

+(void)setOffsetFromCenter:(UIOffset)offset; 

应该HUD内部是一个单例,所以设置了offset后需要重置该参数,避免之后所有的HUD指示器出现位置偏移

+ (void)resetOffsetFromCenter;

总结

SVProgressHUD的显示状态,点击状态等事件还会通过通知进行获取,对于键盘出现的特殊情况也进行了预处理,详细的代码都可以在SVProgressHUD.m中看到,本文只是介绍了各种方法的使用,如果需要对指示器进行更详细的自定义设置可以,通过只是修改源代码实现.

例如修改该方法,对HUD初始化状态的颜色文字等进行配置

- (id)initWithFrame:(CGRect)frame;

修改注意的是,因为文字信息长度的变化会影响到指示器的大小,所以SVProgressHUD对于位置和大小提供了新的方法,在每次修改文字后都会单独调用,而不是通过initWithFrame方法进行配置

- (void)updatePosition; // 更新位置以及大小

值得一提的是SVProgressHUD在更新位置时,设备方向和键盘高度等都会影响到最终位置和大小,这部分代码都在该方向下,大家可以自行查阅.

LipYoung commented 6 years ago

这是2016-05-04写的一篇博客,我现在把他迁移了过来.