from MobileAppMastery/Threading/02
Basically detachNewThreadSelector detaches a thread and starts its work process. When you need to update the UI, you do it on the main thread. We use performSelectorOnMainThread to do it. We make our main thread perform a chunk of code.
1) get the big task going with:
1 2 3 |
[NSThread detachNewThreadSelector:@selector(bigTask) toTarget:self withObject:nil]; |
2) the big task will be running with a ridiculous for loop. Let’s remember to put everything within an autoreleasepool because we want the garbage collection to do its work right away within this cycle.
1 2 3 4 5 6 7 8 9 |
@autoreleasepool { int updateUIWhen = 1000; //whenever for(int i=0;i<100000;i++) { //lots of processing } } |
3) so we’re running happily and this big task is being finished on a separate thread. But what if we want to update the UI? We do it with the performSelectorOnMainThread method:
1 2 3 |
[self performSelectorOnMainThread:@selector(updateProgressViewWithPercentage:) withObject:percentDone waitUntilDone:YES]; |
Basically we run a chunk of code on the main thread. This chunk of code in our case is getting the progress bar to set a new percentage.
Now, the other thing we have to take care of is WHEN do we do this update? In our case, we just do this every time it hits a thousand.
We keep the counter variable ‘updateUIWhen’ to increment by 1000 everytime we update the UI. That way, it i will run 1000 times more before
it does the next update.
Obviously, this is just an example of updating the UI incrementally. You can use whatever method you’d like.
full source:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#import "ViewController.h" @implementation ViewController @synthesize myButton, myActivityIndicator, myProgressView; -(void)updateProgressViewWithPercentage:(NSNumber *)percentDone{ [self.myProgressView setProgress:[percentDone floatValue] animated:YES]; } -(void) bigTask{ @autoreleasepool { int updateUIWhen = 1000; //whenever for(int i=0;i<100000;i++) { NSString *newString = [NSString stringWithFormat:@"i = %i", i]; NSLog(@"%@", newString); //let's if(i == updateUIWhen) { float f = (float)i/10000; NSNumber *percentDone = [NSNumber numberWithFloat:f]; //update our UI using the main thread. [self performSelectorOnMainThread:@selector(updateProgressViewWithPercentage:) withObject:percentDone waitUntilDone:YES]; updateUIWhen = updateUIWhen + 1000; //we update our UI every time we increase by 1000 } } //end of for loops //update UI again main thread. By setting the percentage to 100% [self performSelectorOnMainThread:@selector(updateProgressViewWithPercentage:) withObject:[NSNumber numberWithFloat:1.0] waitUntilDone:YES]; [self.myActivityIndicator stopAnimating]; } } //do task by detaching a new thread (watch UI to see how this works) -(void)bigTaskAction{ [self.myActivityIndicator startAnimating]; [NSThread detachNewThreadSelector:@selector(bigTask) toTarget:self withObject:nil]; } - (void)viewDidLoad{ [super viewDidLoad]; //Create button self.myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; self.myButton.frame = CGRectMake(20, 403, 280, 37); [self.myButton addTarget:self action:@selector(bigTaskAction) forControlEvents:UIControlEventTouchUpInside]; [self.myButton setTitle:@"Do Long Task" forState:UIControlStateNormal]; [self.view addSubview:self.myButton]; //Create activity indicator self.myActivityIndicator = [[UIActivityIndicatorView alloc] init]; self.myActivityIndicator.frame = CGRectMake(142, 211, 37, 37); self.myActivityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge; self.myActivityIndicator.hidesWhenStopped = NO; [self.view addSubview:self.myActivityIndicator]; //Create label self.myProgressView = [[UIProgressView alloc] init]; self.myProgressView.frame = CGRectMake(20, 20, 280, 9); [self.view addSubview:self.myProgressView]; } @end |