For the next app coming from the Cluebucket, I’ll be implementing a theme manager that will manage how the game appears using themes that the user will download from the AppStore.
Among other things, a theme will define which fonts are used in the UI, images and colors. Initially, I had even gone so far as to use the theme to define images that would appear on UIButtons. After putting together a few test themes everything seemed to be working fine, but it felt that defining button images was going a bit too far.
The original thought behind having a theme define button images was giving the graphics designer the capability to define adequate contrast between the background image (defined by the theme) and a few of the standard buttons (help button, next level, previous level) that would appear on the background. This also allowed the GD to define a different look and feel for the buttons to integrate them more nicely with the theme. (Triangles on a geometric theme, hearts on a Valentines theme)
After some testing, it turned out that some buttons should just look the same from theme to theme. It keeps the user experience more consistent (the help button should look like the same help button no mater which theme the user is using). I decided that the buttons would look the same from theme to theme — the only difference between themes would be the color of the image on the button. No problem: I’ll just make a black version and a white version. After all that worked fine for the info button on the iPhone. Right??
Maybe so, but it felt like there could be some middle ground. The GD should have some freedom to color the button to match the theme. That cool Halloween theme on the drawing board might look just fine with a black help button and the Valentines theme might look just fine with a white help button, but the Pirate theme? White doesn’t look good. Neither does black. What we need here is a nice chartreuse.
Core Graphics to the rescue!
So here’s what I did. I defined the standard help button and created a black and white png — this will act as the mask to which I will apply a custom color (help button image mask to the left). After applying the custom color (as defined by the GD in the theme definition), I can return a UIImage that can be scaled appropriately and put onto the help button.
The code to get it done:
First create a rectangular image that is the same size as your image mask:
- (UIImage *) createBackgroundImageWithColor:(UIColor *)color andSize:(CGSize)size {
UIGraphicsBeginImageContext(size); // create a new context to draw in
CGContextRef c = UIGraphicsGetCurrentContext(); // get a reference to the context
CGContextSetFillColorWithColor(c, [color CGColor]); // set the fill color of the context
CGContextFillRect(c, CGRectMake(0.0, 0.0, size.width, size.height)); // draw a rect to fill the context
UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // and create an image from the context
CGContextRelease(c); // don't forget to release the context reference!
return image; // return an autoreleased UIImage
}
Now create a new image using the image created above and your mask image:
- (UIImage *) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {
CGImageRef maskRef = maskImage.CGImage; // get an imageRef for the maskImage
CGImageRef theMask = CGImageMaskCreate(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef), CGImageGetBitsPerComponent(maskRef), CGImageGetBitsPerPixel(maskRef), CGImageGetBytesPerRow(maskRef), CGImageGetDataProvider(maskRef), NULL, false); // create a mask out of the maskRef
// create the maskedImage using another fance CG function
CGImageRef maskedImage = CGImageCreateWithMask([image CGImage], theMask);
// create the image that will be returned
UIImage *returnImage = [UIImage imageWithCGImage:maskedImage];
// release any references that were created
CGImageRelease(theMask);
return returnImage; // return the autoreleased masked image
}
Finally, the theme manager uses the above methods to return the appropriate images for various images in the theme. (OK, maybe not various, but certainly for the help button, next level and previous level buttons).
- (UIImage *) buttonImageUsingImageMaskNamed:(NSString *)imageName {
UIImage *mask = [UIImage imageNamed:imageName];
return [self maskImage:[self createBackgroundImageWithColor:[self buttonColor] andSize:mask.size] withMask:mask];
}
- (UIImage *) helpButtonImage {
return [self buttonImageUsingImageMaskNamed:@"helpbuttonmask.png"];
}
- (UIImage *) nextLevelButtonImage {
return [self buttonImageUsingImageMaskNamed:@"nextLevelButtonMask.png"];
}
- (UIImage *) previousLevelButtonImage {
return [self buttonImageUsingImageMaskNamed:@"previousLevelButtonMask.png"];
}
Final Thoughts
So what have I gained here? I think that I’ve struck a balance in theme definition that allows me to keep some control over what a theme looks like and some of the user interface and experience. I’ve allowed the theme designer some latitude in choosing the right color for the buttons.
And it provided a nice opportunity to use a bit of CG in this project
P.S.
The masking image doesn’t have to be only black and white — you can use shades of gray as well. Black will not mask at all, white masks 100% and shades of gray mask according to their intensity. It is important that your masking image have no alpha channel.