UISpringBoardButton to get started


For a while now, Apple has been delighted in defining the de facto standard for the look of most of the applications in the App store. Which takes me to my next point, when we create a UIButton, we get a look that is pretty much the same always, except when we specify a background image, but then again, we need to comply with several UI rules to be able to use all the buttons and images quite nicely.

There is one type of button that I always liked the way they look, and those are the welcome screen buttons or the SpringBoard. In one of my current projects, I had to mimic how these icons look, needless to say, I went down into Stack Overflow looking for ideas and I did found some nice ideas.

So, the main task was to create some sort of UIButton with a UILabel below placing some sort of caption and showing and also reproducing the shiny icon on the button. To accomplish this, we first need to subclass the UIButton class, which brings us all the basic interactions with a button. So, basically, we'd had something like this:

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "ImageHelpers.h"

@interface UISpringBoardButton : UIButton

//Name of the icon to load from the project
@property (nonatomic, retain) NSString * IconName;

//Title of the button
@property (nonatomic, retain) NSString * ButtonTitle;

//This is where the magic happens
- (id)initWithFrame:(CGRect)frame andIcon:(NSString*) iconName andText:(NSString*) ButtonText;
- (void)initButtonWithSize : (CGSize) buttonSize;
- (void) createButtonWithFrame: (CGRect) buttonFrame;


First, we have 2 properties that will retain the name of the Icon that will represent our button and the Title of the button, which will be displayed in the UILabel below it. For now, I will just focus on getting the job done, and for those of you enjoying "copy + paste" the code was made using XCode 4.2 in the ARC age, so you should remember that. If you are as newb as I was a few weeks ago and didn't knew what the heck ARC was, then consider checking out this. Having said all that, Let's go into the code! First, you should make yourself an extender method for resizing images, I made a class like this one:


//Sorry about the name, old C# habbits...
@interface UIImage (ImageHelpers)

- (UIImage *)getIconOfSize:(CGSize)size withOverlay:(UIImage *)overlayImage;
- (CGContextRef)createBitmapContextOfSize:(CGSize)size;
- (UIImage *)scaleImage:(UIImage *)image toResolution:(int)resolution;


With the exception of the first method, the other two I took them from Fulvio's implementation in his answer this question. Thanks for that Fulvio! Without further ado, this is how the first method would be now:

- (UIImage *)getIconOfSize:(CGSize)size withOverlay:(UIImage *)overlayImage 
    UIImage *icon = [self scaleImage:self toResolution:size.width];
    CGRect iconBoundingBox = CGRectMake (0, 0, size.width, size.height);
    CGRect overlayBoundingBox = CGRectMake (0, 0, size.width, size.height);
    CGContextRef myBitmapContext = [self createBitmapContextOfSize:size];
    CGContextSetRGBFillColor (myBitmapContext, 1, 1, 1, 1);
    CGContextFillRect (myBitmapContext, iconBoundingBox);
    CGContextDrawImage(myBitmapContext, iconBoundingBox, icon.CGImage);
    if ( overlayImage != nil )
        CGContextDrawImage(myBitmapContext, overlayBoundingBox, overlayImage.CGImage);
    UIImage *result = [UIImage imageWithCGImage: CGBitmapContextCreateImage (myBitmapContext)];
    CGContextRelease (myBitmapContext);
    return result;

Now that we have our helper (refered in the #import "ImageHelpers.h" line as well) we can code the button at ease. First, we need to synthetize our properties, coming from a Visual Studio background, I'm still wondering why XCode can't do this for me, but I'm hopeful to see some nice products soon. Sigh... anyways, here it goes:

@implementation UISpringBoardIcon

@synthesize IconName;
@synthesize ButtonTitle;

Good stuff starts now, we need to write a new init method much like the initWithFrame, but with some extra stuff and then we call to the guy that is going to take care of assembling the control:

- (id)initWithFrame:(CGRect)frame andIcon:(NSString*) iconName andText:(NSString*) ButtonText
    self = [super initWithFrame:frame];
    if (self) 
        self.IconName = iconName;
        self.ButtonTitle = ButtonText;
        [self createButtonWithFrame: frame];
    return self;

Once we build the button we call the createButtonWithFrame method that handles creating the UILabel, placing it in the according place, aligning the text in the center and placing the lineBreakMode so that the system handles breaking the text accordingly if is too big.

- (void) createButtonWithFrame: (CGRect) buttonFrame
    CGSize sz = [ButtonTitle sizeWithFont: [UIFont systemFontOfSize: 10]];
    float width = self.frame.size.width;
    float y = self.frame.size.height - sz.height;
    //Need to add some space between the button and the UILabel
    CGRect labelFrame = CGRectMake(0, y + 5, width, sz.height);
    UILabel * textLabel = [[UILabel alloc] initWithFrame: labelFrame];
    textLabel.text = ButtonTitle;
    textLabel.font = [UIFont systemFontOfSize:10];
    textLabel.textAlignment = UITextAlignmentCenter;
    textLabel.lineBreakMode = UILineBreakModeMiddleTruncation;
    [self addSubview:textLabel];
    CGSize buttonSize = CGSizeMake(buttonFrame.size.width, buttonFrame.size.height - sz.height - 5);
    [self initButtonWithSize: buttonSize];

Now, all we need is to create the button using the overlay provided by rpetrich on his answer to the same question stated above. Actually, using Fulvio's code this is a pretty simple process:

- (void)initButtonWithSize : (CGSize) buttonSize
    UIImage *overlayImage = [UIImage imageNamed:@"AppIconOverlay.png"];
    UIImage *image = [UIImage imageNamed: IconName];
    UIImage *profileImage = [image getIconOfSize:CGSizeMake(50, 50) withOverlay:overlayImage];
    self.imageView.layer.masksToBounds = YES;
    self.imageView.layer.cornerRadius = 10.0;
    self.imageView.layer.borderColor = [[UIColor lightGrayColor] CGColor];
    self.imageView.layer.borderWidth = 1.0;
    [self setImage: profileImage forState: UIControlStateNormal];

This is it! Creating a new one is very easy, just one line of code:

  UISpringBoardIcon * button = [[UISpringBoardIcon alloc] 
                                      initWithFrame: frame 
                                            andIcon: @"tux-icon.png"
                                            andText: @"Going home"];

The result should look like this:

Hope this can help! As usual, comenting is not forbidden.

Edit The source code can be downloaded here from my dropbox.

Hello iOS!!!

I've been playing with the idea of doing some mobile development for some time now, but never had the time to do it. Guess what?? I'm really digging into iOS now! For the time being with a borrowed Macbook (they're so expensive!!) but I should get my Mac Mini by the end of this year (Santa has been good to me!). Does this mean that I'm done with PHP or C#? Nope, but I should be really busy with iOS for a while now. Hope I can write something cool soon!!!