在看一些大神寫的開源框架時, 遇到了一個用于記錄進度的類 - NSProgress, 然后通過看官方文檔和網上找資料的方式, 來學習這個類的用法, 并在此記錄此類的用法, 如果有什么地方錯誤請留言, 我會抽空修改。
簡介
- 在iOS7中, 蘋果官方添加了新的類NSProgress。用來報告當前某個任務的進度, 或者多個任務的進度和這些任務的總進度等
- NSProgress可以是一個樹狀結構, 一個節點進度可以有多個子節點, 每一個子節點只有一個父節點, 每一個子節點都有自己獨立的進度體系, 并且子節點完成任務后, 會將完成的數量反饋給父節點
基本屬性
- totalUnitCount: 總單元, 用來記載某個任務的總單元數 (可以理解為某個任務正常結束時,
需要完成的任務總量
) - completedUnitCount: 已完成單元數量, 記載某個任務執行過程中已經完成的單元數量 (可以理解為, 某一個任務在執行過程中
已經完成的任務量
) - fractionCompleted: 某個任務
已完成單元量
占總單元量
的比例 - localizedDescription: 通過
字符串的形式
描述當前任務完成度
- 格式:
100% completed
- 格式:
- localizedAdditionalDescription: 同localizedDescription一樣, 用來描述
當前任務的完成度
- 格式:
9 of 10
(總任務量為10, 已完成任務量為9, 即 完成量/總量)
- 格式:
通過基本屬性和KVO, 創建簡單的單進度報告操作
#import "ViewController.h"
@interface ViewController ()
/** 進度 */
@property (nonatomic, strong) NSProgress *progress;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化進度對象, 并設置進度總量
self.progress = [NSProgress progressWithTotalUnitCount:10];
//使用KVO觀察fractionCompleted的改變
[self.progress addObserver:self forKeyPath:@"fractionCompleted" options:(NSKeyValueObservingOptionNew) context:nil];
}
/**
當點擊根視圖時觸發
*/
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//定時器
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(task:) userInfo:nil repeats:YES];
//加到當前運行循環
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
/**
定時器調用的方法
*/
- (void)task:(NSTimer *)timer
{
if (self.progress.completedUnitCount >= self.progress.totalUnitCount) {
[timer invalidate];
return;
}
self.progress.completedUnitCount += 1;
}
/**
KVO回調方法
*/
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
{
//獲取觀察的新值
CGFloat value = [change[NSKeyValueChangeNewKey] doubleValue];
//打印
NSLog(@"fractionCompleted --- %f, localizedDescription --- %@, localizedAdditionalDescription --- %@", value, self.progress.localizedDescription, self.progress.localizedAdditionalDescription);
}
@end
- 當點擊控制器view的時候, 會有如下打印
進度打印.png
添加子節點的方法 (IOS9之前)
- - (void)becomeCurrentWithPendingUnitCount:(int64_t)unitCount;
- 該方法是用來在當前節點中分出一個子節點, 當子節點完成時, 總的進度完成量自動加上unitCount
- unitCount: 子節點完成時, 總任務完成的量
- - (void)resignCurrent;
- 與
- (void)becomeCurrentWithPendingUnitCount:(int64_t)unitCount;
配套使用, 在這兩個方法中間需要設置至少一個子節點, 當子節點完成后, 總完成量會自動增加unitCount量, 如果不設置子節點, 那么總任務完成量直接增加unitCount - 注意點: 這兩個方法成對出現, 并且必須在同一個隊列中調用
- 與
- 多節點示例代碼:
#import "ViewController.h"
//繼承自NSProgress的類, 添加一個唯一標識key
@interface LTProgress : NSProgress
/** 唯一標識 */
@property (nonatomic, copy) NSString *key;
@end
@implementation LTProgress
@end
@interface ViewController ()
/** 進度 */
@property (nonatomic, strong) NSProgress *progress;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化進度對象, 并設置進度總量
self.progress = [NSProgress progressWithTotalUnitCount:10];
// 分出5個任務量給任務一
[self.progress becomeCurrentWithPendingUnitCount:5];
[self subTask:@"任務一"];
[self.progress resignCurrent];
// 分出5個任務量給任務二
[self.progress becomeCurrentWithPendingUnitCount:5];
[self subTask: @"任務二"];
[self.progress resignCurrent];
}
- (void)subTask:(NSString *)key
{
// 每個子任務的任務量分為10個單元, 每完成一個任務單元, 總任務完成量加上 5.0 / 10.0 = 0.5的任務單元
LTProgress *subProgress = (LTProgress *)[LTProgress progressWithTotalUnitCount:10];
subProgress.key = key;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(task:) userInfo:@{@"subProgress" : subProgress} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)task:(NSTimer *)timer
{
//獲取當前的進度
NSDictionary *userInfo = timer.userInfo;
LTProgress *subProgress = userInfo[@"subProgress"];
//當完成量達到總量時停止任務
if (subProgress.completedUnitCount >= subProgress.totalUnitCount) {
[timer invalidate];
return;
}
//模仿完成1份
subProgress.completedUnitCount += 1;
//打印
NSLog(@"fractionCompleted --- %f, localizedDescription --- %@, localizedAdditionalDescription --- %@, key:%@", self.progress.fractionCompleted, self.progress.localizedDescription, self.progress.localizedAdditionalDescription, subProgress.key);
}
@end
- 具體打印如下:
分出子任務.png
- 當一個節點分出兩個子節點時, 給每個子節點分配了需要完成的單元量, 只要子節點完成, 總節點的進度就會自動增加對應的節點單元量, 而子節點又可以將這些分配到的單元量重新進行分配
iOS9中的優化
- 在IOS9中新增了以下方法, 用來給節點添加子節點, 不用再使用上述兩個方法
+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount parent:(NSProgress *)parent pendingUnitCount:(int64_t)portionOfParentTotalUnitCount
- (void)addChild:(NSProgress *)child withPendingUnitCount:(int64_t)inUnitCount
- 可以通過這兩個方法直接分出子任務進度
- 示例代碼:
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化進度對象, 并設置進度總量
self.progress = [NSProgress progressWithTotalUnitCount:10];
NSProgress *sub1 = [NSProgress progressWithTotalUnitCount:10 parent:self.progress pendingUnitCount:5];
NSProgress *sub2 = [NSProgress progressWithTotalUnitCount:10 parent:self.progress pendingUnitCount:5];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(task:) userInfo:@{@"sub1" : sub1, @"sub2" : sub2} repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)task:(NSTimer *)timer
{
//獲取當前的進度
NSDictionary *userInfo = timer.userInfo;
NSProgress *sub1 = userInfo[@"sub1"];
NSProgress *sub2 = userInfo[@"sub2"];
//當完成量達到總量時停止任務
if (sub1.completedUnitCount >= sub1.totalUnitCount && sub2.completedUnitCount >= sub2.totalUnitCount) {
[timer invalidate];
return;
}
//模仿完成1份
sub1.completedUnitCount += 1;
NSLog(@"fractionCompleted --- %f, localizedDescription --- %@, localizedAdditionalDescription --- %@ --- sub1", self.progress.fractionCompleted, self.progress.localizedDescription, self.progress.localizedAdditionalDescription);
sub2.completedUnitCount += 1;
//打印
NSLog(@"fractionCompleted --- %f, localizedDescription --- %@, localizedAdditionalDescription --- %@ --- sub2", self.progress.fractionCompleted, self.progress.localizedDescription, self.progress.localizedAdditionalDescription);
}
- 控制臺打印:
iOS9分出子進度的打印.png
- 屬性 and 方法
// 獲取當前線程的進度管理對象根節點
// 注意:當有NSProgress對象調用了becomeCurrentWithPendingUnitCount:方法后,這個方法才能獲取到
+ (nullable NSProgress *)currentProgress;
// 創建NSProgress對象, 并設置進度單元的總數
+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount;
// iOS9后使用 創建NSProgress對象, 并設置進度單元的總數
+ (NSProgress *)discreteProgressWithTotalUnitCount:(int64_t)unitCount NS_AVAILABLE(10_11, 9_0);
/**
iOS9后使用, 創建一個NSProgress對象, 并指定父節點, 當完成時父節點完成單元數量 + portionOfParentTotalUnitCount
@param unitCount 任務進度的單元數量
@param parent 父節點, 可傳入nil
@param portionOfParentTotalUnitCount 自身完成時, 父節點進度總量增加的值, 即父節點分配的任務量
@return NSProgress對象, 是parent的子節點
*/
+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount parent:(NSProgress *)parent pendingUnitCount:(int64_t)portionOfParentTotalUnitCount NS_AVAILABLE(10_11, 9_0);
// iOS9后使用, 同上
- (instancetype)initWithParent:(nullable NSProgress *)parentProgressOrNil userInfo:(nullable NSDictionary *)userInfoOrNil NS_DESIGNATED_INITIALIZER;
// 注冊為當前線程根節點, 并分配子節點的任務量
- (void)becomeCurrentWithPendingUnitCount:(int64_t)unitCount;
// 取消注冊 與注冊方法必須同步出現, 必須在同一個線程內
- (void)resignCurrent;
// iOS9后使用, 向一個節點中添加一個子節點
- (void)addChild:(NSProgress *)child withPendingUnitCount:(int64_t)inUnitCount NS_AVAILABLE(10_11, 9_0);
#pragma mark *** Reporting Progress ***
// 進度單元總數
@property int64_t totalUnitCount;
// 進度單元已完成數
@property int64_t completedUnitCount;
// 描述進度 例如: 80% completed
@property (null_resettable, copy) NSString *localizedDescription;
// 描述進度 例如: 2 of 10 (2為已完成單元數量, 10為總單元數量)
@property (null_resettable, copy) NSString *localizedAdditionalDescription;
// 是否可以取消
@property (getter=isCancellable) BOOL cancellable;
// 是否可以暫停
@property (getter=isPausable) BOOL pausable;
// 是否已經取消
@property (readonly, getter=isCancelled) BOOL cancelled;
// 是否已經暫停
@property (readonly, getter=isPaused) BOOL paused;
// 進度取消回調
@property (nullable, copy) void (^cancellationHandler)(void);
// 進度暫停回調
@property (nullable, copy) void (^pausingHandler)(void);
// 進度恢復回調
@property (nullable, copy) void (^resumingHandler)(void) NS_AVAILABLE(10_11, 9_0);
#pragma mark *** Observing and Controlling Progress ***
@property (readonly, getter=isIndeterminate) BOOL indeterminate;
// 進度比例 0 - 1之間
@property (readonly) double fractionCompleted;
// 取消
- (void)cancel;
// 暫停
- (void)pause;
// 恢復
- (void)resume NS_AVAILABLE(10_11, 9_0);