Creating full-screen iOS modal views

Jun 13, 2011 19:59 · 450 words · 3 minute read iOS iphone objective-C

This is another “blog it so I’ll remember it” post, but it might come in useful should it ever get Googled.

Scenario: I’m running an asynchronous data load in the background which populates a table view.  Although the data load is asynchronous, because it updates the table I want to place a modal popup with an activity spinner over the current table view so that interaction is blocked.  I’m using Sam Vernette’s SVProgressHUD, which is a really neat implementation of the Apple segmented spinner thing.  The modal popup has to cover the whole of the window so that the table can’t be manipulated while the data load is in progress.

Problem: If you add the spinner into the context of the table view, the x and y coordinates of the spinner are anchored to the top of the table view.  This means that if you’re scrolled down, the spinner will be displayed towards the top of the screen - or even worse, off the top of the screen if the table view is a long one.

Solution: Instead of adding the spinner to the table view, add it to the main window.  This has three stages:

  1. Get a reference to the main window

  2. Create a UIView of the correct size

  3. (Optional) add the SVProgressHUD to the UIView you’ve just created

  4. (Optional) set up the properties of the UIView - background colour, alpha etc

  5. Add the UIView you just created as a subview of the main window

The code looks like this:

// Get main window reference
UIWindow* mainWindow = (((YourAppDelegate *)[UIApplication sharedApplication].delegate).window);

// Create a full-screen subview
UIView *spinnerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];

// Set up some properties of the subview
spinnerView.backgroundColor = [UIColor whiteColor];
spinnerView.alpha = 0.5f;

// Optional - display the SVProgressHUD
[SVProgressHUD showInView:spinnerView status:@"Loading..."];

// Add the subview to the main window
[mainWindow addSubview:throbberView];

// Release the subview
[spinnerView release];

The key bit is getting the reference to the main window, with the somewhat-less-than obvious reference to the app delegate. That’s the full extent of the device screen rather than any subviews below it, so adding a subview to this means that you’re anchored relative to the device.  Of course, you’re not restricted to covering the whole window - changing the subview’s frame means you can move things around.

Dismissing it is more or less the reverse:

[spinnerView removeFromSuperview];

In this particular app the spinnerView needs to be dismissed in a delegate method that gets triggered by the asynchronous load finishing.  To make life easier I created an instance variable in the controller to hold the subview so I could get hold of it and dismiss it in the context of the delegate method.