阅读本文前,请先去下载源代码(传送门)
MBProgressHUD是一个iOS的类,当等待后台进程执行时,可以显示出一个包含指示器或者标签的半透明的HUD。HUD是UIKit包中不允许开发者使用的UIProgressHUD的替代品。
使用前提
MBProgressHUD可以以ARC或者non-ARC方式运行在iOS的任何版本,它使用到下列Cocoa Touch框架
我们从MBProgressHUD.h看起,首先MBProgressHUD继承子UIView,它具有View的特性,其头文件中,定义的枚举类型MBProgressHUDMode和MBProgressHUDAnimation,分别对应不同的加载等待框样式和消失动画类型。通过接下来的宏定义,来标志ARC和non-ARC模式及对BLOCK的支持。通过类方法+ (MBProgressHUD *)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; 或者通过对象方法alloc及initWithView可以获得MBProgressHUD对象,可以通过MBProgressHUD的delegate属性,将遵循MBProgressHUDDelegate协议的对象,设为委托,在MBProgressHUD将要隐藏时回调。
在程序遇到异步或者耗时较长的任务需要执行时,可以使用- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;方法,“锁住“主界面,并显示加载等待框。重点变在该函数内部,我们看一下:
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { methodForExecution = method; targetForExecution =MB_RETAIN(target); objectForExecution =MB_RETAIN(object); // Launch execution in new thread self.taskInProgress =YES; [NSThreaddetachNewThreadSelector:@selector(launchExecution)toTarget:selfwithObject:nil]; // Show HUD view [selfshow:animated]; }
首先,我们将要执行的任务及目标对象传入后,MB_RETAIN实际只是宏,在之前说的那段头文件中,在non-ARC下,它将先将target和object的内存计数加1,熟悉内存管理的应该知道,接到参数后先retain,才能防止对象被提前释放。接着使用NSThread开一个线程,这是重点,现在有了新的一条线程,他将去执行我们的传进来的任务,可不让任务在主线程中调用,这样才不会阻塞主线程。新线程发消息给launchExecution。
- (void)launchExecution { @autoreleasepool { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" // Start executing the requested task [targetForExecutionperformSelector:methodForExecutionwithObject:objectForExecution]; #pragma clang diagnostic pop // Task completed, update view in main thread (note: view operations should // be done only in the main thread) [selfperformSelectorOnMainThread:@selector(cleanUp)withObject:nilwaitUntilDone:NO]; } }
cleanUp执行一些release和赋值nil操作后,将执行- (void)hide:(BOOL)animated这个将去执行触发动画效果的方法,接着将调用- (void)done,在done中有一段这样的代码
if ([delegaterespondsToSelector:@selector(hudWasHidden:)]) { [delegateperformSelector:@selector(hudWasHidden:)withObject:self]; }
向我们代理的对象发送hudWasHidden:消息,如果之前设置这确,便可以响应。
在头文件中有
@property (atomic, copy) NSString *labelText; @property (atomic, copy) NSString *detailsLabelText;
我们对这两个NSString类型的属性的赋值,会直接更新界面中的Label,这里使用到的是KVO编程,通过#pragma mark - KVO标记可以查看该部分代码,所有键值信息
- (NSArray *)observableKeypaths { return [NSArray arrayWithObjects:@"mode", @"customView", @"labelText", @"labelFont", @"detailsLabelText", @"detailsLabelFont", @"progress", nil]; }
注册键值
- (void)registerForKVO { for (NSString *keyPath in [self observableKeypaths]) { [self addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew context:NULL]; } }
当键值改变时,更新UI
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (![NSThread isMainThread]) { [self performSelectorOnMainThread:@selector(updateUIForKeypath:) withObject:keyPath waitUntilDone:NO]; } else { [self updateUIForKeypath:keyPath]; } }