Please note, this blog entry is from a previous course. You might want to check out the current one.
The user-interface should always be giving some indication (e.g. a UIActivityIndicatorView) to the user when the currently displaying user-interface is going to show something when some active thread finishes. The network activity indicator in the status bar is not sufficient for this, but your application should also turn on the networkActivityIndicator whenever it is accessing the network (and only then).
The table view will use the refresh-control feature in the following thus – there is no need to put an extra activity indicator there for now.
Change in both storyboards the background of the scroll views to black (which is a cosmetic change only) … the rest will be done in code to avoid having to duplicate everything for both storyboards.
Create a new property for the activity indicator, and initialize it lazily. Note to be able to center the spinner using auto-layout it is essential to switch of its auto-resize mask:
@property (nonatomic, strong) UIActivityIndicatorView *spinner; ... - (UIActivityIndicatorView *)spinner { if (!_spinner) { _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; [self.view addSubview:_spinner]; [_spinner setTranslatesAutoresizingMaskIntoConstraints:NO]; NSDictionary *variables = NSDictionaryOfVariableBindings(_spinner); [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[_spinner]-|" options:0 metrics:nil views:variables]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[_spinner]-|" options:0 metrics:nil views:variables]]; } return _spinner; }
Enable the spinner when there is an image to load, and disable it after the download process and (if there is any) the image setup. The network-activity indicator is enabled only for the actual download:
- (void)resetImage { if (self.scrollView) { ... if (!self.imageURL) return; [self.spinner startAnimating]; ... dispatch_async(queue, ^{ [NetworkActivityIndicator start]; NSData *imageData = [[NSData alloc] initWithContentsOfURL:self.imageURL]; [NetworkActivityIndicator stop]; if (imageURL == self.imageURL) { dispatch_async(dispatch_get_main_queue(), ^{ ... [self.spinner stopAnimating]; }); } }); } }
The network-activity indicator is also enabled during the data download for the first table view controller:
- (void)viewDidLoad { ... dispatch_async(queue, ^{ [NetworkActivityIndicator start]; NSArray *photos = [FlickrFetcher stanfordPhotos]; [NetworkActivityIndicator stop]; ... }); }
When two or more photos are downloaded in different threads at the same time and they access the network-activity indicator, one of them could switch it off even if another download is still active. While this could be synchronized using a local property in the image view controller, it could also happen that there is another data download for the table view is active. Thus the code above uses a new class derived from NSObject. Its public interface consists of two class methods to start and stop the indicator:
+ (void)start; + (void)stop;
Those increase or decrease a counter:
+ (void)start { [self counterChange:1]; } + (void)stop { [self counterChange:-1]; }
The counter is implemented using a static variable which – using Grand Central Dispatch – can only be accessed by one caller at a time. Depending on its changed value it will switch off or on the indicator.
+ (void)counterChange:(NSUInteger)change { static NSUInteger counter = 0; static dispatch_queue_t queue; if (!queue) { queue = dispatch_queue_create("NetworkActivityIndicator Queue", NULL); } dispatch_sync(queue, ^{ if (counter + change <= 0) { counter = 0; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; } else { counter += change; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; } }); }
The complete code is available on github.