閱讀本文前,請先去下載原始碼(傳送門)
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];}}