Details of the iOS method that USES the NSProgress class to create the UI progress bar

  • 2020-06-07 05:21:37
  • OfStack

1. The introduction

Prior to iOS7, system 1 did not provide a complete framework to describe task scheduling related functions. This makes it difficult to monitor the progress of time-consuming tasks in development. After iOS7, the NSProgress class is provided to report the progress of tasks.


2. Create a single-task progress listener

Monitoring the progress of a single task is the simplest application scenario of NSProgress. Let's simulate a time-consuming task with a timer. The sample code is as follows:


@interface ViewController ()
{
 NSProgress * progress;
}
@end

@implementation ViewController

- (void)viewDidLoad {
 [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
 // This method creates the task progress management object  UnitCount is 1 Based on UI The number of units for the complete task on 
 progress = [NSProgress progressWithTotalUnitCount:10];
 NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(task) userInfo:nil repeats:YES];
 // Monitor the completion rate of the task progress object 
 [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

{
 NSLog(@" The progress of = %f",progress.fractionCompleted);
}
-(void)task{
 // Number of units completed +1
 
 if (progress.completedUnitCount<progress.totalUnitCount) {
  progress.completedUnitCount +=1;
 }
 
}

In the sample code above, the fractionCompleted attribute is a floating-point value between 0 and 1, representing the completion ratio of the task. There are also two string-type properties in the NSProgress object that translate the progress information into a fixed format:


// After display ratio   Such as: 10% completed
@property (null_resettable, copy) NSString *localizedDescription;
// Complete the number   Such as: 1 of 10
@property (null_resettable, copy) NSString *localizedAdditionalDescription;

3. Create a multi-task progress listener

In fact, in development, there are often many subtasks in a single task. NSProgress is designed in a tree structure to support the nesting of subtasks. Examples are as follows:


- (void)viewDidLoad {
 [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
 // This method creates the task progress management object  UnitCount is 1 Based on UI The number of units for the complete task on 
 progress = [NSProgress progressWithTotalUnitCount:10];
 // Monitor the completion rate of the task progress object 
 [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
 // Branch down 1 subtasks   The total subtask progress is 5 A unit   That is, when the subtask is complete   The father progerss Object progress 5 A unit 
 [progress becomeCurrentWithPendingUnitCount:5];
 [self subTaskOne];
 [progress resignCurrent];
 // Downward parting regulation 2 subtasks 
 [progress becomeCurrentWithPendingUnitCount:5];
 [self subTaskOne];
 [progress resignCurrent];
}

-(void)subTaskOne{
 // There are subtasks 10 A unit 
 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 this tree design pattern at first glance is really some puzzling, one point to note, becomeCurrentWithPendingUnitCount: method is the meaning of this NSProgress object registration for the current thread task schedule management object, the root of resignCurrent method for cancellation of registration, these two methods must come in pairs, when a NSProgress object is registered for the current thread root node, the method using class progressWithTotalUnitCount: NSProgress objects are created by default as a child node is added.

4. New design method for multi-task progress monitoring after iOS9

As the example above illustrates, the registration of the root node way poor readability, code structure also not too clear, may Apple engineers also think so, after the iOS9 NSProgress class added 1 method, through these methods can be more clearly express the progress indicator, the hierarchical structure between the example code is as follows:


- (void)viewDidLoad {
 [super viewDidLoad];
 // Do any additional setup after loading the view, typically from a nib.
 // This method creates the task progress management object  UnitCount is 1 Based on UI The number of units for the complete task on 
 progress = [NSProgress progressWithTotalUnitCount:10];
 // Monitor the completion rate of the task progress object 
 [progress addObserver:self forKeyPath:@"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
 // Create child nodes 
 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 ++;
 }
}

As shown in the code above, the code structure is clearer and more operable.

5. 1 Small summary


// Gets the root node of the progress management object for the current thread 
// Note: When there is NSProgress Object called becomeCurrentWithPendingUnitCount: The method can only be obtained after the method 
+ (nullable NSProgress *)currentProgress;
// create 1 a NSProgress Object, the number of units that need to pass in progress 
+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount;
// And on the 1 The functions of the methods are similar  iOS9 And then the new method 
+ (NSProgress *)discreteProgressWithTotalUnitCount:(int64_t)unitCount;
//iOS9 And then the new method   Creates a child node of a progress indicator node 
+ (NSProgress *)progressWithTotalUnitCount:(int64_t)unitCount parent:(NSProgress *)parent pendingUnitCount:(int64_t)portionOfParentTotalUnitCount;
//NSProgress Method to initialize an instance   The self-parent node parameter can be nil
- (instancetype)initWithParent:(nullable NSProgress *)parentProgressOrNil userInfo:(nullable NSDictionary *)userInfoOrNil;
// Register as the current thread root node 
- (void)becomeCurrentWithPendingUnitCount:(int64_t)unitCount;
// Cancel the registration   Must occur in sync with the registration method 
- (void)resignCurrent;
//iOS9 The new method   to 1 Add to the nodes 1 Child node 
- (void)addChild:(NSProgress *)child withPendingUnitCount:(int64_t)inUnitCount;
// Total number of progress units 
@property int64_t totalUnitCount;
// The number of progress units completed 
@property int64_t completedUnitCount;
// Can you cancel it? 
@property (getter=isCancellable) BOOL cancellable;
// Can it be suspended 
@property (getter=isPausable) BOOL pausable;
// Progress proportion  0-1 between 
@property (readonly) double fractionCompleted;
// cancel 
- (void)cancel;
// suspended 
- (void)pause;
// restore 
- (void)resume

6. User configuration dictionary for NSProgress objects

In the user dictionary of NSProgress object, 1 specific key value can be set to set the display mode. Examples are as follows:


// Set the remaining time   Will affect the localizedAdditionalDescription The value of the 
/*
 Such as: 0 of 10  -  About 10 seconds remaining
*/
[progress setUserInfoObject:@10 forKey:NSProgressEstimatedTimeRemainingKey];
// Set completion speed information   Will affect the localizedAdditionalDescription The value of the 
/*
 Such as: Zero KB of 10 bytes (15 bytes/sec)
*/
[progress setUserInfoObject:@15 forKey:NSProgressThroughputKey];
/*
 The following key values take effect   Must be NSProgress The object's kind Properties are set to  NSProgressKindFile
NSProgressFileOperationKindKey The key corresponds to the prompt text type   Will affect the localizedDescription The value of the 
NSProgressFileOperationKindKey The optional corresponding values are as follows: 
NSProgressFileOperationKindDownloading :   According to Downloading files ... 
NSProgressFileOperationKindDecompressingAfterDownloading :   According to Decompressing files ... 
NSProgressFileOperationKindReceiving :   According to Receiving files ... 
NSProgressFileOperationKindCopying :   According to Copying files ... 
*/
 [progress setUserInfoObject:NSProgressFileOperationKindDownloading forKey:NSProgressFileOperationKindKey];
/*
NSProgressFileTotalCountKey Key sets the total number of files to display  
 Such as: Copying 100 files ... 
*/
 [progress setUserInfoObject:@100 forKey:NSProgressFileTotalCountKey];
// Sets the number of completions 
[progress setUserInfoObject:@1 forKey:NSProgressFileCompletedCountKey];

7. Show the progress step summary in UI
Here are some steps to display progress in a view or view controller:
1. Before you invoke a long-running task, create an instance of NSProgress using the +progressWithTotalUnitCount:. Method. The parameter totalUnitCount will include "total number of units of work to be completed".

It is important to fully understand this value from the perspective of the UI layer; You won't be asked to guess how many actual work objects there are and how many kinds of units of work (bytes? Pixels? Number of lines?) . If you traverse the collection and plan to call the instance object for every element of the collection, the argument will often be 1 or perhaps the number of elements in a collection.

2. Use KVO to register an observer with the fractionCompleted attribute of a progress. Similar to NSOperation,NSProgress was designed for use with the aid of KVO. In MAC, this makes it very easy to bind an instance of NSProgress to a progress bar or tag via Cocoa Bindings. On iOS, you will manually update your UI in KVO observer handle.

In addition to the fractionCompleted, completedUnitCount and totalUnitCount attributes, NSProgress also has 1 localizedDescription (@"50% completed") and 1 localized Additional Description (@"3 of 6") that can be bound to text tags. KVO notifications are sent in the thread that changes the property value of the NSProgress object, so be sure to update UI manually in your main thread.

3. The current progress object creates a new progress object by calling -ES106en:. Here, the pendingUnitCount parameter is equivalent to "part 1 of the total amount of work to be done by the recipient". You can call this method multiple times and pass one part of totalUnitCount each time. In the iteration example of a collection element, we would call [progress becomeCurrentWithPendingUnitCount:1] every iteration;

4. Call a method on a work object. Since the current progress is a local thread concept, you must do this in the same thread you called becomeCurrentWithPendingUnitCount:. This is not a problem if API of the work object is designed to be called in the main thread, as I see most API (Brent Simmons).

But if your UI layer is creating a background queue and calling a work object to synchronize that queue, be sure to call becomeCurrentWithPendingUnitCount: and resignCurrent in the same dispatch_async() block.

5. Call -resignCurrent in your progress object. This method corresponds to -becomeCurrentWith PendingUnitCount: and will be called the same number of times. You can call resignCurrent before the actual work is done, so you don't have to wait until you get a completion notification from the work object.


Related articles: