iOS中使用NSProgress類來創(chuàng)建UI進度條的方法詳解
一、引言
在iOS7之前,系統(tǒng)一直沒有提供一個完整的框架來描述任務(wù)進度相關(guān)的功能。這使得在開發(fā)中進行耗時任務(wù)進度的監(jiān)聽將什么麻煩,在iOS7之后,系統(tǒng)提供了NSProgress類來專門報告任務(wù)進度。
二、創(chuàng)建單任務(wù)進度監(jiān)聽器
單任務(wù)進度的監(jiān)聽是NSProgress最簡單的一種運用場景,我們來用定時器模擬一個耗時任務(wù),示例代碼如下:
@interface ViewController () { NSProgress * progress; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //這個方法將創(chuàng)建任務(wù)進度管理對象 UnitCount是一個基于UI上的完整任務(wù)的單元數(shù) progress = [NSProgress progressWithTotalUnitCount:10]; NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(task) userInfo:nil repeats:YES]; //對任務(wù)進度對象的完成比例進行監(jiān)聽 [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"進度= %f",progress.fractionCompleted); } -(void)task{ //完成任務(wù)單元數(shù)+1 if (progress.completedUnitCount<progress.totalUnitCount) { progress.completedUnitCount +=1; } }
上面的示例代碼中,fractionCompleted屬性為0-1之間的浮點值,為任務(wù)的完成比例。NSProgress對象中還有兩個字符串類型的屬性,這兩個屬性將進度信息轉(zhuǎn)化成固定的格式:
//顯示完后比例 如:10% completed @property (null_resettable, copy) NSString *localizedDescription; //完成數(shù)量 如:1 of 10 @property (null_resettable, copy) NSString *localizedAdditionalDescription;
三、創(chuàng)建多任務(wù)進度監(jiān)聽器
上面演示了只有一個任務(wù)時的進度監(jiān)聽方法,實際上,在開發(fā)中,一個任務(wù)中往往又有許多子任務(wù),NSProgress是以樹狀的結(jié)構(gòu)進行設(shè)計的,其支持子任務(wù)的嵌套,示例如下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //這個方法將創(chuàng)建任務(wù)進度管理對象 UnitCount是一個基于UI上的完整任務(wù)的單元數(shù) progress = [NSProgress progressWithTotalUnitCount:10]; //對任務(wù)進度對象的完成比例進行監(jiān)聽 [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; //向下分支出一個子任務(wù) 子任務(wù)進度總數(shù)為5個單元 即當(dāng)子任務(wù)完成時 父progerss對象進度走5個單元 [progress becomeCurrentWithPendingUnitCount:5]; [self subTaskOne]; [progress resignCurrent]; //向下分出第2個子任務(wù) [progress becomeCurrentWithPendingUnitCount:5]; [self subTaskOne]; [progress resignCurrent]; } -(void)subTaskOne{ //子任務(wù)總共有10個單元 NSProgress * sub =[NSProgress progressWithTotalUnitCount:10]; int i=0; while (i<10) { i++; sub.completedUnitCount++; } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSLog(@"= %@",progress.localizedAdditionalDescription); }
NSProgress的這種樹狀設(shè)計模式乍看起來確實有些令人費解,有一點需要注意,becomeCurrentWithPendingUnitCount:方法的意義是將此NSProgress對象注冊為當(dāng)前線程任務(wù)的根進度管理對象,resignCurrent方法為取消注冊,這兩個方法必須成對出現(xiàn),當(dāng)一個NSProgress對象被注冊為當(dāng)前線程的根節(jié)點時,后面使用類方法 progressWithTotalUnitCount:創(chuàng)建的NSProgress對象都默認作為子節(jié)點添加。
四、iOS9之后進行多任務(wù)進度監(jiān)聽的新設(shè)計方法
正如上面的例子所演示,注冊根節(jié)點的方式可讀性很差,代碼結(jié)構(gòu)也不太清晰,可能Apple的工程師們也覺得如此,在iOS9之后,NSProgress類中又添加了一些方法,通過這些方法可以更加清晰的表達進度指示器之間的層級結(jié)構(gòu),示例代碼如下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //這個方法將創(chuàng)建任務(wù)進度管理對象 UnitCount是一個基于UI上的完整任務(wù)的單元數(shù) progress = [NSProgress progressWithTotalUnitCount:10]; //對任務(wù)進度對象的完成比例進行監(jiān)聽 [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil]; //創(chuàng)建子節(jié)點 NSProgress * sub = [NSProgress progressWithTotalUnitCount:10 parent:progress pendingUnitCount:5]; NSProgress * sub2 = [NSProgress progressWithTotalUnitCount:10 parent:progress pendingUnitCount:5]; for (int i=0; i<10; i++) { sub.completedUnitCount ++; sub2.completedUnitCount ++; } }
如上面代碼所示,代碼結(jié)構(gòu)變得更加清晰,可操作性也更強了。
五、一點小總結(jié)
//獲取當(dāng)前線程的進度管理對象根節(jié)點 //注意:當(dāng)有NSProgress對象調(diào)用了becomeCurrentWithPendingUnitCount:方法后,這個方法才能獲取到 + (nullable NSProgress *)currentProgress; //創(chuàng)建一個NSProgress對象,需要傳入進度的單元數(shù)量 + (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount; //和上一個方法功能相似 iOS9之后的新方法 + (NSProgress *)discreteProgressWithTotalUnitCount:(int64_t)unitCount; //iOS9之后的新方法 創(chuàng)建某個進度指示器節(jié)點的子節(jié)點 + (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount parent:(NSProgress *)parent pendingUnitCount:(int64_t)portionOfParentTotalUnitCount; //NSProgress實例的初始化方法 自父節(jié)點參數(shù)可以為nil - (instancetype)initWithParent:(nullable NSProgress *)parentProgressOrNil userInfo:(nullable NSDictionary *)userInfoOrNil; //注冊為當(dāng)前線程根節(jié)點 - (void)becomeCurrentWithPendingUnitCount:(int64_t)unitCount; //取消注冊 與注冊方法必須同步出現(xiàn) - (void)resignCurrent; //iOS9新方法 向一個節(jié)點中添加一個子節(jié)點 - (void)addChild:(NSProgress *)child withPendingUnitCount:(int64_t)inUnitCount; //進度單元總數(shù) @property int64_t totalUnitCount; //已完成的進度單元數(shù) @property int64_t completedUnitCount; //是否可取消 @property (getter=isCancellable) BOOL cancellable; //是否可暫停 @property (getter=isPausable) BOOL pausable; //進度比例 0-1之間 @property (readonly) double fractionCompleted; //取消 - (void)cancel; //暫停 - (void)pause; //恢復(fù) - (void)resume
六、關(guān)于NSProgress對象的用戶配置字典
在NSProgress對象的用戶字典中可以設(shè)置一些特定的鍵值來進行顯示模式的設(shè)置,示例如下:
//設(shè)置剩余時間 會影響localizedAdditionalDescription的值 /* 例如:0 of 10 — About 10 seconds remaining */ [progress setUserInfoObject:@10 forKey:NSProgressEstimatedTimeRemainingKey]; //設(shè)置完成速度信息 會影響localizedAdditionalDescription的值 /* 例如:Zero KB of 10 bytes (15 bytes/sec) */ [progress setUserInfoObject:@15 forKey:NSProgressThroughputKey]; /* 下面這些鍵值的生效 必須將NSProgress對象的kind屬性設(shè)置為 NSProgressKindFile NSProgressFileOperationKindKey鍵對應(yīng)的是提示文字類型 會影響localizedDescription的值 NSProgressFileOperationKindKey可選的對應(yīng)值如下: NSProgressFileOperationKindDownloading: 顯示Downloading files… NSProgressFileOperationKindDecompressingAfterDownloading: 顯示Decompressing files… NSProgressFileOperationKindReceiving: 顯示Receiving files… NSProgressFileOperationKindCopying: 顯示Copying files… */ [progress setUserInfoObject:NSProgressFileOperationKindDownloading forKey:NSProgressFileOperationKindKey]; /* NSProgressFileTotalCountKey鍵設(shè)置顯示的文件總數(shù) 例如:Copying 100 files… */ [progress setUserInfoObject:@100 forKey:NSProgressFileTotalCountKey]; //設(shè)置已完成的數(shù)量 [progress setUserInfoObject:@1 forKey:NSProgressFileCompletedCountKey];
七、在UI中顯示進度步驟總結(jié)
以下有幾個在視圖或者視圖控制器中顯示進度的步驟:
1.在你調(diào)用一個長時間運行的任務(wù)之前,借助+progressWithTotalUnitCount:.方法建立一個NSProgress實例。 參數(shù)totalUnitCount將會包括“要完成的總工作單元的數(shù)量”。
有一點很重要,要從UI圖層的角度完全理解這個數(shù)值;你不會被要求猜測有多少個實際工作對象以及有多少種類的工作單元(字節(jié)?像素?文字行數(shù)?)。如果你遍歷集合并且計劃為每一個集合元素調(diào)用該實例對象,該參數(shù)經(jīng)常會是1或者也許是一個集合中的元素的數(shù)量 。
2.使用KVO注冊一個進度的fractionCompleted屬性的觀察者。類似于NSOperation,NSProgress被設(shè)計借助KVO來使用。在MAC,這使得通過Cocoa Bindings綁定一個NSProgress實例到一個進度條或者標(biāo)簽上變得非常容易。在iOS上,你將會在KVO observer handle中手動更新你的UI。
除了fractionCompleted, completedUnitCount和totalUnitCount屬性之外,NSProgress也有一個localizedDescription (@"50% completed"),并且還有一個localized Additional Description (@"3 of 6"),其能夠被綁定到文本標(biāo)簽。KVO通知在改變NSProgress對象屬性值的線程中發(fā)送,因此確保在你的主線程中手動更新UI。
3.當(dāng)前的進度對象通過調(diào)用-becomeCurrentWithPendingUnitCount:方法建立新的進度對象。在這里,pendingUnitCount這個參數(shù)相當(dāng)于“是要被接收者完成的總的工作單元的量要完成的工作的一部分”。你可以多次調(diào)用這個方法并且每次傳遞totalUnitCount(本次代碼完成的占比)的一部分。在集合元素的迭代示例中,我們將會在每一次迭代中調(diào)用[progress becomeCurrentWithPendingUnitCount:1];
4.調(diào)用工作對象的方法。由于當(dāng)前進度是一個局部線程概念,你必須在你調(diào)用becomeCurrentWithPendingUnitCount:的相同的線程中做這個事情。如果工作對象的API被設(shè)計成在主線程中調(diào)用,那這就不是一個問題,就像我對大部分API的看法那樣(Brent Simmons 也這么認為)。
但是如果你的UI 層正在建立一個后臺隊列并且調(diào)用工作對象來同步那個隊列,那要確保將 becomeCurrentWithPendingUnitCount:和resignCurrent放到相同的dispatch_async()塊中調(diào)用。
5.在你的進度對象中調(diào)用-resignCurrent。這個方法是和-becomeCurrentWith PendingUnitCount:相對應(yīng)的,并且會調(diào)用相同的次數(shù) 。你可以在實際工作被完成以前調(diào)用resignCurrent,因此你不需要等待,直到你得到一個來自工作對象的完成通知。
相關(guān)文章
iOS UITableView 拖動排序?qū)崿F(xiàn)代碼
這篇文章主要為大家詳細介紹了iOS UITableView 拖動排序?qū)崿F(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09iOS開發(fā)網(wǎng)絡(luò)篇—實現(xiàn)大文件的多線程斷點下載
iOS開發(fā)中經(jīng)常會用到文件的下載功能,這篇文章主要介紹了iOS開發(fā)網(wǎng)絡(luò)篇—實現(xiàn)大文件的多線程斷點下載,今天咱們來分享一下思路。2016-11-11IOS 通過tag刪除動態(tài)創(chuàng)建的UIButton
這篇文章主要介紹了IOS 通過tag刪除動態(tài)創(chuàng)建的UIButton的相關(guān)資料,需要的朋友可以參考下2017-03-03Flutter開發(fā)Widgets?之?PageView使用示例
這篇文章主要為大家介紹了Flutter開發(fā)Widgets?之?PageView使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10