Custom Loading View
A common want in any application that communicates with a web service is to add a loading/loader view to show your user that the application is busy getting data and has not crashed. I've come up with a generic loading view that I use on multiple applications. It's split into two methods: one to add the loading view and one to remove it.
- (void)startLoadingAnimationWithMessage:(NSString *)message tag:(NSUInteger)tag;
- (void)stopLoadingAnimationWithTag:(NSUInteger)tag;
In the above signatures, we see that when creating a loading view we pass it an NSString object so that the loading view can be specific to the operation the application is performing e.g. "Requesting Data..." or "Logging in...".
In the example below, the methods could be either added via a category or inherited from a superclass.
Let's dig into the methods now:
- (void)startLoadingAnimationWithMessage:(NSString *)message tag:(NSUInteger)tag
{
CGFloat activityIndicatorPadding = 15.0f;
// new transparent view to disable user interaction during operation.
UIView *activityView = [[UIView alloc] initWithFrame:self.view.bounds];
activityView.tag = tag;
activityView.backgroundColor = [UIColor colorWithRed:(0.0/255.0) green:(59.0/255.0) blue:(102.0/255.0) alpha:1.0];
activityView.alpha = 0.75;
// Loader spinner
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[activityView addSubview:activityIndicator];
[activityIndicator release];
UILabel *requestingInformation = [[UILabel alloc] init];
requestingInformation.text = message;
requestingInformation.backgroundColor = [UIColor clearColor];
[activityView addSubview:requestingInformation];
[requestingInformation release];
CGSize requestingInformationSize = [requestingInformation.text sizeWithFont:requestingInformation.font constrainedToSize:CGSizeMake(9999, 9999) lineBreakMode:requestingInformation.lineBreakMode];
CGFloat totalWidthOfUIElements = (requestingInformationSize.width + activityIndicator.frame.size.width + activityIndicatorPadding);
CGFloat activityIndicatorStartX = (self.view.bounds.size.width - totalWidthOfUIElements) / 2;
activityIndicator.center = CGPointMake((activityIndicatorStartX + (activityIndicator.frame.size.width / 2)), (self.view.bounds.size.height / 2));
requestingInformation.frame = CGRectMake((activityIndicator.frame.origin.x + activityIndicator.frame.size.width + activityIndicatorPadding), ((self.view.bounds.size.height / 2) - 10), requestingInformationSize.width, requestingInformation.font.lineHeight);
[self.view addSubview:activityView];
[self.view bringSubviewToFront:activityView];
[activityView release];
[activityIndicator startAnimating];
}
So the loader view is made up of activityIndicator (a UIActivityIndicatorView), requestingInformation (a UILabel) and a backing view to hold all of the elements, activityView (a UIView).
So in the above we create activityView, set its frame to fill the current view, add a background colour and make the whole view semi-transparent. We also take advantage of the versatile tag property to uniquely identify that view.
We then move on to creating activityIndicator. Here, we initialise it with a style and add it to our activityView object, remembering to clean up after ourselves by releasing the object.
Next, we create our label, assign the NSString that we passed in and work out where in relation to the activityIndicator object it should appear in the UI based upon its width. We then set where the activityIndicator will appear by setting its center.
Next we add activityView to our current view and ensure that it is at the front.
Finally, we begin animating the UIActivityIndicatorView.
- (void)stopLoadingAnimationWithTag:(NSUInteger)tag
{
UIView *activityView = [self.view viewWithTag:tag];
for (UIView *subview in [activityView subviews])
{
if ([subview isKindOfClass:[UIActivityIndicatorView class]])
{
UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)subview;
[activityIndicator stopAnimating];
break;
}
}
[activityView removeFromSuperview];
}
Here, we remove the activityView from the current view and stop the UIActivityIndicatorView object's animation using the tag property that we set.