读源码系列----MBProgressHUD(加载等待框)

来源:互联网 发布:标识符c语言 编辑:程序博客网 时间:2024/05/19 07:41

阅读本文前,请先去下载源代码(传送门)

MBProgressHUD是一个iOS的类,当等待后台进程执行时,可以显示出一个包含指示器或者标签的半透明的HUD。HUD是UIKit包中不允许开发者使用的UIProgressHUD的替代品。

使用前提

MBProgressHUD可以以ARC或者non-ARC方式运行在iOS的任何版本,它使用到下列Cocoa Touch框架

  • Foundation.framework
  • UIKit.framework
  • CoreGraphics.framework

我们从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 threadself.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];}}

targetForExecution完成具体任务的执行,这时候该线程在一直执行我们的任务,performSelectorOnMainThread的意思是在主线程执行cleanUp,所以当我们任务在其他线程执行完,回到主线程时,执行cleanUp。

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];}}