I have been doing some iOS development again lately, and I am writing some posts about what I have learned. The previous post covered some general tips, this one is going to be about design/interface tips for some common iOS classes.

This article is for people who played around in XCode and objectiveC already, and are familiar with the basic iOS UIView and related classes.

Thanks!

The project these tips are applied to are my language classes for iPhone and iPad, such as Spanish Class, French Class and German Class. If you want to thank me for this article, please download a free version, and send me your feedback or give it a 4 or 5 star rating if you like the app.

Tip 1 – UIView: drawing an image as a background

Although you can not set an image as the background of a UIView in an interface builder xib file, it is easy to load the image as a color and assign it as the backgroundcolor to a view:

UIView *mainView = self.view;
mainView.backgroundColor = [UIColor colorWithPatternImage:
     [UIImage imageNamed: @"languages-menu.png"]];

Which results in the following background for the main menu UIView of the languages app:

Tip 2 – UIButton: fixing the position of the image and the label within

As shown in the screenshot above, I wanted to put the image and text within a main menu button at a specific location, so the images in the 10 different buttons would appear symmetric to each other.

This is very easy to do with the following method:

- (void) fixButton:(UIButton *) button{
    //positioning
    UIImageView *imageView = button.imageView;
    UILabel *label = button.titleLabel;

    CGRect imageFrame = imageView.frame;
    CGRect labelFrame = label.frame;

    imageFrame.origin.x = 10;
    labelFrame.origin.x = 10 + imageFrame.size.width + 5;

    imageView.frame = imageFrame;
    label.frame = labelFrame;
}

We are changing the x values of the origin of the frames of the two views within the button, making the image start at x coordinate 10 within the UIButton, and putting the label next to it, with only 5 pixels in between.

The key thing to realize here is that every view – UIView and subclasses like UITableView, UIButton,… – are positioned on the screen the same way: they have a frame, with an origin with an x and an y coordinate and a size with a width and a length. Changing their values is easy, and not considered bad practice.
Unlike as for Android, where you want to use relative layouts to accomodate for the wide range of possible screen sizes and shapes, playing with these values is common practice on iOS, since you know everything about the screensize. There are only 4 possible resolutions anyway: landscape and portrait, both for iPhone and iPad.

Note: One more thing about UIButtons that beats me is why Apple didnt provide an easy way to change the coloring of the basic white button. You need to use either the white button, use an image(which means firing up Adobe Illustrator) or some complicated way of putting a view on top of the image and masking it. Apple, please add plain recoloring of buttons!

Tip 3 – UITableView: clearing the background of a table

In the exercises screens, I wanted to present the questions on a schoolboard interface, as if the questions and possible answers were written on the schoolboard. The list of possible answers is wrapped in a UITableView, so it is easy to change the number of answers(they used to be presented as a UIPicker, but that just looked awful, and there is no way to achieve the below effect with a picker anyway).

To do this, I had to manually clear the background of the tableview, which I implemented as an Objective-C category:

#import "UITableView+RemoveBackgroundFromTable.h"

@implementation UITableView (RemoveBackgroundFromTable)

-(void) removeBackgroundFromTable{
    self.backgroundColor = [UIColor clearColor];
    self.opaque = NO;
    self.backgroundView = nil;
}

@end

The first two lines can be set in a xib file, by unchecking “opaque” and by setting the color of the UITableView to “clearColor”. The last line cannot be done in a xib file though and is necessary as well. Not only the backgroundColor needs to be set to transparant, the backgroundView of the UITableView needs to be set to nil as well.

Tip 4 – UITableview: showing cells with dynamic height

When doing iOS development, you will want to tamper with the height of cells sooner or later. The UITableViewCells do not adjust their height automatically, and when the cell spans a few sentences, you cannot set the height in advance, because you just dont know it.

Credit were credit is due, this post describes exactly how to resize cells to accommodate different cell heights. However, since I was working with my own UITableViewCell subclasses which usually had another view to the left, I had to rewrite the StringHelper category a bit and add an xOffset:

@implementation NSString (StringHelper)

- (CGFloat)RAD_textHeightForSystemFontOfSize:(CGFloat)size xOffset:(int) xOffset{
    //Calculate the expected size based on the font and linebreak mode of the label
    CGFloat maxWidth = [UIScreen mainScreen].bounds.size.width - 25 - xOffset;
    CGFloat maxHeight = 9999;
    CGSize maximumLabelSize = CGSizeMake(maxWidth,maxHeight);

    CGSize expectedLabelSize = [self sizeWithFont:[UIFont systemFontOfSize:size] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeWordWrap];

    return expectedLabelSize.height;
}

- (CGRect)RAD_frameForCellLabelWithSystemFontOfSize:(CGFloat)size xOffset:(int) xOffset{
    CGFloat width = [UIScreen mainScreen].bounds.size.width - 25 - xOffset;
    CGFloat height = [self RAD_textHeightForSystemFontOfSize:size xOffset:xOffset] + 10.0;
    return CGRectMake(10.0f+xOffset, 10.0f, width, height);
}

- (void)RAD_resizeLabel:(UILabel *)aLabel WithSystemFontOfSize:(CGFloat)size xOffset:(int) xOffset{
    aLabel.frame = [self RAD_frameForCellLabelWithSystemFontOfSize:size xOffset:xOffset];
    aLabel.text = self;
    [aLabel sizeToFit];
}

@end

To make sure the cell resizes properly, it is then only necessary to resize the UILabel in your tableView: cellForRowAtIndexPath: method:

- (UITableViewCell *)tableView:(UITableView *)tableView
		 cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        CustomCell *cell = [self getCustomCell];
        NSUInteger row = [indexPath row];

        NSString *element = [list objectAtIndex:row];

        cell.customLabel.numberOfLines = 0;
        [element RAD_resizeLabel:cell.inTargetLanguage WithSystemFontOfSize:17 xOffset:40.0];

        CGRect secondFrame = cell.customLabel.frame;
        secondFrame.origin.y = secondFrame.origin.y + cell.customLabel.frame.size.height + 10;
        cell.customLabel.frame = secondFrame;

        return cell;
}

and to calculate the height, in the tableView:heightForRowAtIndexPath: method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath  {
	NSUInteger row = [indexPath row];
        NSString *element = [list objectAtIndex:row];
        CGFloat height = [element RAD_textHeightForSystemFontOfSize:17 xOffset:40.0] + 10.0;
        return height;
}

Tip 5 – UIFont: using the new fancy fonts but providing a callback if they are not available

iOS provides two chalk fonts, and I wanted to use any of those for the schoolboard: ChalkDuster and ChalkboardSE. However, these fonts are not available on all iOS devices.

Therefore, I check which ones are available in a FontUtil class and return accordingly:

+(UIFont*) getChalkFontWithSize:(int) size{
    if ([[UIFont fontNamesForFamilyName:@"ChalkDuster"] count] > 0){
        if ([[UIFont fontNamesForFamilyName:@"ChalkDuster"] containsObject:@"ChalkDuster"]){
            return [UIFont fontWithName:@"ChalkDuster" size:size];
        }
        else {
            return [[UIFont fontNamesForFamilyName:@"ChalkDuster"] objectAtIndex:0];
        }
    }
    else if ([[UIFont fontNamesForFamilyName:@"Chalkboard SE"] count] > 0){
        if ([[UIFont fontNamesForFamilyName:@"ChalkboardSE-Regular"] containsObject:@"ChalkboardSE-Regular"]){
            return [UIFont fontWithName:@"ChalkboardSE-Regular" size:size];
        }
        else {
            return [[UIFont fontNamesForFamilyName:@"Chalkboard SE"] objectAtIndex:0];
        }
    }
    else {
        return [UIFont systemFontOfSize:size];
    }
}

Another tip: to have a quick overview of all the iOS fonts, check www.iosfonts.com.

Tip 6 – UIColor: getting a color from an hex string

If you want to port an app from Android – we are integrating stuff here after all -, where colors are defined in their rgb values, this Stackoverflow Question gives a nice category on UIColor, which makes it possible to call:

[UIColor: colorWithHexString: @"#800000"]

returning a UIColor matching the hex rgb string.

Tip 7 – UIAlertView: recoloring the alert

To recolor the basic blue UIAlertView, you need to subclass it and override layoutSubViews. Again, I do not get why Apple did not build this feature into XCode, since it seems to me something a lot of developers want to do.

This blog post gives an excellent implementation of such a CustomAlertView.

You can then just set the color to #80000 or black to get a darkred or black color like this:

-(void) showRedAlert: (NSString *) message andTitle:(NSString *) title{
    [CustomAlertView setBackgroundColor:[UIColor colorWithHexString:@"800000"]
                        withStrokeColor:[UIColor whiteColor]];
	UIAlertView *alert = [[CustomAlertView alloc] initWithTitle:title
                                                        message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
	[alert show];
	[alert release];
}

Resulting in alertviews looking like this: