When performing an action that may take some time, it’s advised to show something to signal that the operation is being performed & the application is not crashing. Here’s an example on how to accomplish a loading indicator as used in the iPhone Facebook application.

First we’ll create the actual loading view, let’s add an empty XIB to our Xcode project called LoadingView. Add a UIView to the nib and drag a UIImageView onto it. Use a vector program like Inkscape to create a black rectangle with rounded corners. Use a UILabel and a UIActivityIndicator to create something like the screenshot to the right. Be sure to check the animating property of the activity indicator and change the alpha of the root view to 0.8 for intance, since we want the view to be slightly transparant.
Once we’ve made the actual view, it’s time to show it.
- (UIView*)newLoadingView { NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"LoadingView" owner:self options:nil]; NSEnumerator *enumerator = [nib objectEnumerator]; id object; while ((object = [enumerator nextObject])) { if ([object isMemberOfClass:[UIView class]]) { return object; } } return nil; } - (void)doWork { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Add lenghty operation. [loadingView removeFromSuperview]; [pool release]; } - (void)someMethod { loadingView = [self newLoadingView]; loadingView.center = CGPointMake(160, 350); [self.view addSubview:loadingView]; [NSThread detachNewThreadSelector:@selector(doWork) toTarget:self withObject:nil ]; }
We’re using the newLoadingView: method to retrieve the view from the nib, the reason we’re looping through the contents of the nib is one cannot garantee the order in which items are retrieved will always be the same. For instance the index of the loading view will be different in firmware version 2.0 and 2.1. We retrieve a UIView item, in this case the loading view, since it’s the only UIView in the nib. I’m sure this code could be improved though, using your own derived UIView and checking for this derived class would be better. The center property of the loading view can be used to position the view, the top left corner has coordinates 0,0. (If anyone has a better method of getting a specific view from a nib, do tell)
The someMethod method is the one to call to start everything, this method will show the actual loading view and start the doWork method on a seperate thread. The reason we’re using a seperate thread is not to lock up the interface, otherwise the interface would freeze while the lenghty operation would be performed. The doWork method hides the loading view once everything is done.
Extension: The above code has one disadvantage, when using a tab controller the loading view will disappear once another tab is selected, returning to the original tab will show the loading view again though. In some cases this behaviour isn’t wanted, how do we solve this? Instead of showing the loading view on top of the content view displayed by the tab controller, we’ll show it on top of all the views, on top of the tab controller.
// Show loading view on top of everything [[UIApplication sharedApplication].keyWindow addSubview:loadingView];
And that’s all there is to it.
Update: here’s the correct method to load a view from a separate nib file.
Tags: Facebook, threading, UIActivityIndicator
If you subclass the UIwindow-class you can add the overlay as an UIAlertView, this will remain on top of everything. Even tab controllers
You should probably be doing all your view hierarchy manipulation on the main thread.
Using a ViewController to manage Nibs makes memory management of the objects insode the Nib so much easier. You are responsible for the memory of ‘top-level’ objects inside the Nib.
If you use an IBOutlet ivar of type UIView in the class above, in the Nib you can just hook up the view to File’s Owner. The objects will be connected at runtime when the Nib is loaded. You then won’t need to programatically loop through all the objects in the Nib.