Fun with gradient masks and the iOS status bar

Developing an app in iOS which displays the status bar comes with a few challenges. Since it occupies a small amount of space at the top of the viewport, you have to consider it in your design. If you have an app with scrolling content for example, then at some point you probably had to deal with this.

This isn’t going to work. The status bar is clashing with our content. Now there are many different ways to handle this but I’m going to show you a really clever way using an alpha gradient mask and CAGradientLayer. Here is the result.

Now something like this could be done by preparing assets in Photoshop, but I’m going to show you how to do it programmatically. I will assume that you already know how to set up a UICollectionView, if not there is a link to the full source at the end of the article.

Let’s get started. Create a new collection view and set its frame to take up the entire screen. Then setup a new UIImageView with your background image and set it as the backgroundView of the collection view.

CGRect bounds = [[self view] bounds];
collectionView = [[UICollectionView alloc] initWithFrame:bounds];
// ... set delegate, datasource, etc.
[[self view] addSubview:collectionView];
[collectionView release];
 
// Set our "outer space" background image
UIImage *background = [UIImage imageNamed:@"space-background"];
UIImageView *backgroundView = [[UIImageView alloc] initWithImage:background];
[collectionView setBackgroundView:backgroundView];
[backgroundView release];

The next step is to crop and save a small portion of the background image. For this we can use CGImageCreateWithImageInRect. This function will create an image from an image, in the specified rectangle.

UIImage *background = [(id)[collectionView backgroundView] image];
// This is the rectangle of the image that the status bar is covering
// we also need to adjust it for scale.
CGRect barRect = CGRectMake(0.0f, 0.0f, 320.0f, 28.0f);
barRect.size.width *= [background scale];
barRect.size.height *= [background scale];
 
// Create an image from the barRect area and convert it to a UIImage 
CGImageRef imageRef = CGImageCreateWithImageInRect([background CGImage], barRect);
UIImage *topImage = [UIImage imageWithCGImage:imageRef
                                        scale:[background scale]
                                  orientation:UIImageOrientationUp];
CGImageRelease(imageRef);

If you put a break point after the topImage code, you can mouse over to see what the image looks like in memory.

Looks good! Now we need to create a view for this image to go on top of our collection view, and then apply an alpha gradient mask to it. For this we can use a CAGradientLayer with 2 colors: one transparent, one opaque – and by setting the startPoint and endPoint to cover the bottom.

// Create transparent -> opaque gradient
CAGradientLayer *alphaGradientLayer = [CAGradientLayer layer];
NSArray *colors = [NSArray arrayWithObjects:
                   (id)[[UIColor colorWithWhite:0 alpha:0] CGColor],
                   (id)[[UIColor colorWithWhite:0 alpha:1] CGColor],
                   nil];
[alphaGradientLayer setColors:colors];
 
// Start the gradient at the bottom
[alphaGradientLayer setStartPoint:CGPointMake(0.0f, 1.0f)];
// And go almost half way up.
[alphaGradientLayer setEndPoint:CGPointMake(0.0f, 0.6f)];
 
// Create a image view for the topImage and apply the mask
statusBarView = [[UIImageView alloc] initWithImage:topImage];
[alphaGradientLayer setFrame:[statusBarView bounds]];
[[statusBarView layer] setMask:alphaGradientLayer];
 
// Finally, add the masked image view on top
[[self view] addSubview:statusBarView];
[statusBarView release]

That will do it. Now if you run your app you’ll see your content fade nicely as it slides underneath the status bar, and since we did it in code you could easily apply this to any or all of the views in your application.

Full source can be found on GitHub.