Custom loading view

February 2nd, 2011
#ui

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 came up with a generic loading view that I use on multiple applications, its 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 a NSString object so that the loading view that be specific to the operation the application is performing e.g. "Requesting Data..." or "Logging in...".

In the below example the methods could be used with either a category or inherited from a super class.

Lets 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: [[UIScreen mainScreen] 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.frame.size.width - totalWidthOfUIELements)/2;

    activityIndicator.center = CGPointMake(activityIndicatorStartX,(self.view.frame.size.height/2)); 
    requestingInformation.frame = CGRectMake((activityIndicator.frame.origin.x + activityIndicator.frame.size.width + activityIndicatorPadding), (self.view.frame.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 a UIActivityIndicatorView - activityIndicator, UILabel - requestingInformation and a backing view to hold all of the elements UIView - activityView.

So in the above we create activityView, set its frame to be that one of the iOS screen size, add a background colour and set that background semi-transparent. We also take advantage of the versatile tag property to uniquely identify that view.

We then move onto creating activityIndicator, here we init 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 settings its frame.

Next we add activityView to our current view and ensure that it 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 properties that we set.

What do you think? Let me know by getting in touch on Twitter - @wibosco