Learn to Code iOS Apps 4: Making It Beautiful

Mike Jaoudi Mike Jaoudi

Learn how to make an iOS app.

Welcome to Part 4 of the Learn to Code iOS Apps series! This series introduces you to the basics of programming with the goal of creating an iOS app; no prior programming experience is required.

In the first three tutorials in the series, you learned the basics of programming in Objective-C, and making a simple iOS app. Specifically:

  • In Part 1, you learned the basics of Objective-C programming and you created a simple command line number guessing game.
  • In Part 2, you learned about objects and classes in Objective-C and you created a simple app to track people’s names and ages.
  • In Part 3, the real fun begins! Here you took everything that you learned about Objective-C programming and created a simple iPhone game of your own.
  • In Part 4 (You are Here!), you will take this app and make it beautiful, learning more about customizing the look and feel of iPhone apps.

In the previous tutorial in the series, you created a simple but fun game where you furiously tried to tap a button as fast as possible.

However, the app didn’t look all that good! In this tutorial, you’ll take that app and add some awesome-sauce to it :]

Specifically, you are going to learn how to add custom images and sounds to the game to give the player the sharp gaming experience they are used to from other iOS apps.

This tutorial will pick up where the last tutorial left off. If you don’t have it already, here’s the project you developed in the previous tutorial.!

Why Bother?! The Game is Done!

You may be thinking “Meh, why bother working on this anymore?! No new features are going to be added to the game. Why does the game need custom images and custom sound?! I have better things to do… like Xbox!”

Well, take a look at this next picture, and see if you still have the same impression:

Which app do you think someone would be more likely to buy? :]

The picture on the left is the end result of Part 1 — the image on the right is the result of adding custom graphics and sound. As you can see, the extra effort you’ll expend in this tutorial to extend your app will definitely be worth it – plus you’ll learn a lot along the way!

Adding custom images to your app will always give it a more polished and professional appearance. Remember, your app is competing with a million others; an app that is intuitive and pleasing to look at is almost as important as intuitive and pleasing code! :]

Double Density

To get started, download the resources for this project. Many thanks to Vicki Wenderlich for making these images!

After you download the file, unzip it and take a look at the images stored inside:

Notice how there are two copies of each image. The smaller image is used for devices with regular displays (older iPhones and iPads) and the other is scaled to double the height and width for Retina displays (the crisp display on newer iPhones and iPads).

The Retina display density is twice that of a non-Retina display, so a 20×20 image (with 400 pixels) needs a 40×40 image (1600 pixels) to take advantage of the high-quality Retina display. Newer iOS devices have Retina displays so it’s important to include these higher-resolution images.

What happens if you only include the normal resolution files, you ask? In that case, images in your app will look slightly blurry or blocky, as in the example below:

Handling multiple versions of graphics files must require extra coding, right? Nope! (Chuck Testa!) :]

Apple uses a special file naming convention where you just add “@2x” to the end of your file name before the extension. For example, if you have a file named image.png, the retina version would be named image@2x.png, as below:

Then when you’re writing code or using storyboards, just refer to the image name without the @2x added onto it. The app will figure out what kind of device it’s running on and automatically pick the retina version if it’s available! Pretty slick! :]

Getting Started

Start Xcode and open up the project from Part 1. Again if you don’t have it already, you can snag it here.

First, you’ll add all of the images and sounds to the project. To do this, select all of the items from the folder to where you downloaded them previously, and drag them into the area where all your files are in Xcode. It makes the most sense to put your graphical and sound resources in onto the Supporting Files group, as below:

Make sure that you check the box at the top that says Copy Items into Destination Group’s Folder, then click Finish.

By selecting the “Copy items” checkbox, Xcode will make a copy of all the images and store it in your project directory. This is important because it will make your project still work even if you delete the files from their original location where you downloaded them, or move that folder somewhere else. It’s a good practice to keep everything an Xcode project needs inside its directory.

Excellent! Now all of the sounds and images are in the project and ready to be used. Since you’re manipulating the new graphical elements of your project, it will make sense to work with them in your Storyboard layout! :]

Images that Tell a Story(board)

Below is your storyboard, just as you left it from Part 1 of this tutorial. The “Tap Me” button will be the first thing to get a makeover. Select the button and have a look at its Attributes in the right sidebar. Remember, the Attributes tab is the fourth from the left and looks like a little slider:

Find the State Config attribute for the button near the top of the list:

Buttons can have several different states in your application. There are four different states that your button can have: Default, Highlighted, Selected and Disabled.

  • Default – The button is not pressed
  • Highlighted – The button is pressed
  • Selected – The button has been switched on, but does not have to be pressed. Used for “toggle” switches for example.
  • Disabled – The button has been disabled and cannot be pressed

The ones you’ll use most often are Default and Highlighted – since they mean pressed and unpressed.

And indeed, the “Tap Me” button will have a different look for the Default and Highlighted states. Make sure the State Config is set to Default, and then set the Background attribute to button_tap_deselected.png. Remember, you are using the filename without the @2x added onto it!

Now take a look at your fancy new game button!

Uh…that looks terrible! The button has two overlapping titles! What’s going on here?!

Ah — the image AND the button both have text indicating the action. You need to turn off the button title. In the attributes for your image, delete the “Tap Me” text from the Title.

The button looks better -but the sizing is a little off. What should be a nice circular button is a little squished! :]

You could resize the button directly in the storyboard just by looking at it, but your design sense should be tingling at this point. Remember, you’re dealing with an image that has an exact size: 228 by 240 pixels, in this case. It makes sense to enter the exact size of the button right into the storyboard.

Make sure the button is still selected, and switch to the Size tab in the right sidebar. It is the second button from the right and it has a ruler icon:

Change the Width to 228 and the Height to 240.

Hey — the button image now looks great! Make sure to position the button on the storyboard to the center of the screen, and make sure it isn’t overlapping the other two labels on the screen.

Okay! Now that the size and positioning of the button is set, you still need to change what the button looks like when it’s pressed. To do that, you will use a different image to represent the depressed state.

Go back to the Attributes tab and set the State Config to Highlighted, as below:

Now set the Background to button_tap_selected.png

Great — it looks like your fancy new button is ready to go! :] Click the Run button in the upper left corner and see it in action:

Wonderful! Your app is starting to look a lot better with that one set of images added!

The rest of your app is looking a little dated in comparison, isn’t it? Time to give the rest of it a makeover! :]

Image Frenzy!

You’ve just learned how to add images as button background images — but what if you don’t need a button and just want the image? Image Views to the rescue! As the name suggests, an Image View is a view that can display images.

Awesome! Sounds like you can use an Image View to make your app look even better! Go into the object library and drag an image view into the storyboard, like so:

It would look nice if you had some borders at the top and bottom of the screen — a nice checkerboard pattern has been provided for you.

Drag an Image View into the main view. Place one at the top of the main view, and one along the bottom of the view. You’ll set the width and height more precisely in just a moment, so just roughly resize them to stretch across the width of the view and make them a little shorter, as pictured here:

Super! Now you need to set the image to be displayed in each of the two Image Views. Click on the top image view and go to the Attributes column. Set the Image to checker_top.png.

Now you’ll need to set the Image View to be the the correct size for your screen. Go to the Size tab. Set the Width to 320 and the Height to 22.

That looks a bit better! Now you just need to do the same thing to the bottom Image View.

Click on the bottom image view and go into the Attributes column. Then set the Image to checker_bottom.png, as shown:

Now set the bottom Image View to the proper size! Go to the Size tab, and set the Width to 320 and the Height to 22.

Time to check out how things look in the app! Hit the Run button in the upper left corner:

So far so good! Your app is starting to look better — but that awful green color in the background is a real bummer, now that you have your shiny new graphics in place!

Time to repaint the walls of your app! :]

Backgrounds, Revisited

In Part 1, you set the background color of the main view to green. While setting a background color is easy, there isn’t a real easy way to set a background image right on the storyboard. Time to leave the world of storyboards for a bit and return to the code!

Open up ViewController.m and have a look at the viewDidLoad method. Remember that this method is called right after all the views are created. It is also called before all of the views are displayed on the screen.

Hm…this sounds like the perfect place to set up your background image! :]

When you wanted to change the text displayed in a label, you had to set up outlets with the IBOutlet keyword. Now, you want to change the background of the view. Do you need to setup another outlet for the background image?

No — fortunately, the project automatically sets up an outlet for you, so you can go ahead and change the background color. Update your viewDidLoad method like this:

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
 
    self.view.backgroundColor = [UIColor purpleColor]; // ADD THIS!!
 
    [self setupGame];
}

Since this is a ViewController, self refers to the View Controller; the background view is view; finally, the view’s background color is backgroundColor.

Note: In code, you refer to color by way of the UIColor class. Colors are typically stored as separate red, green and blue (RGB) values, since any color can be those three basic colors in various combinations. Rather than mix and match color values, UIColor has some built-in shortcuts such as purpleColor that you’re using here, just to make your life easy! Wasn’t that nice of them? :]

Run your app! You should now see a nice purple background as below:

Wow.

Well, the good news is you now know how to set the background color from code.

The bad news? That purple looks horrible! I didn’t think it could possibly get worse than that neon green ;]

UIColor has one more trick up its sleeve: aside from solid colors, it can take any image and convert it to a pattern that you can use in the background! If the image is smaller than the background, it will repeat or tile the image so it covers the entire view.

Perhaps that feature can save you from this purple haze? :]

Change the code for the background color in viewDidLoad to the following:

  self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"bg_tile.png"]];

Rather than a specific color like green or purple, you’re calling colorWithPatternImage to get a pattern from an image. Images are stored as UIImage objects, so here you are setting up an UIImage with the “bg_tile.png” image, which will be the background for the app.

Run your app now — does it look any better?

Aww yeah — that looks a whole lot better! If you don’t see the nice background image, make sure the file name is spelled correctly in the code you changed above.

There are two more background images available for you to use in your app: one is for the timer label, and the other is for the score label.

Add these two lines to the viewDidLoad method, just after the line you added above which sets the view’s background:

  scoreLabel.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"field_score.png"]];
  timerLabel.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"field_time.png"]];

The pattern is set in exactly the same manner, except the views use different images.

That’s it! Now run your project:

Whoa! This is problematic. The text was a little hard to read before — but now it is practically impossible to read!

If the background image is smaller than the view, UIColor will try to help you out by tiling, or repeating, the image; however, if the background image is too large, it will be cut off.

Just as you set the “Tap Me” button size right in the storyboard, you can do the same for the two labels. Open up MainStoryboard.storyboard and select the timer label. In the Size tab of the Utilities sidebar, set the Width to 133 and set the Height to 46, as such:

Now do the same thing for the score label. Select the score label, set the Width to 146, and then set the Height to 102, like so:

Excellent! Now run the app and have a look.

Well, it’s a little better, but the text is still really hard to read! Perhaps a lighter color would work better for the text on a dark background?

Open up Mainstoryboard.storyboard and select the timer label. In the Attributes tab, click on the black square next to Color to open up the color palette, as below:

In the color palette, select the Sliders tab and then select RGB Sliders from the pulldown menu. This will let you enter the red, green, and blue values for the label separately.

Set Red to 190, Green to 255 and Blue to 255, like this:

Note: After you set the color with the RGB sliders, the text might change to light blue or the entire label might turn into a light blue rectangle. Either result is okay! The color is being set and remember, the background image will be set from code in the viewDidLoad method.

Do the same set of steps for the score label. Select the score label, click on its color in the attributes to open up the color palette, and set the RGB slider Red to 190, Green to 255 and Blue to 255, like so:

Now run the project! Will this be it?

That looks great! Now not only have you made your first iOS app, but you’ve made a beautiful looking app! :]

Apps just aren’t about looks though — they need to sound great as well! To put the final polish on your app, you’ll be adding some music and sound effects in the next section.

Adding Sound

Music and sound effects are great ways to add character to your app. There are three sounds you’ll use in your app: background music, a beep every time the player taps the button, and a beep for every second that passes on the countdown clock — just to make the player sweat a little! :]

iOS supports all kinds of features such as GPS, accelerometer, and sound playback. Each of these features is bundled into something called a framework. You’re already using a framework called UIKit, which includes views, view controllers, buttons, storyboards, etc.

To play sounds, there’s a framework called AVFoundaton that you’ll need to use. To add it to the project, click on the Tap Me project at the top of the file browser in the left sidebar, as below:

Then click Tap Me under targets, and select the Summary tab:

Scroll down until you find a section called Linked Frameworks and Libraries. This is a list of all the frameworks currently in your app. To add more frameworks, click the + (plus sign) button.

Whoa — that’s a lot of frameworks to choose from!

Find AVFoundation.framework in the list and select it. Then click the Add button, like this:

Awesome! Now the AVFoundation framework has been added to the project. You’re ready to get grooving with some sound! :]

The sound playback will be handled from the view controller code, so the first step is to set up the header file. Open up ViewController.h. Near the top of the file, you’ll notice this line:

#import <UIKit/UIKit.h>

Hey, look at that! There’s the import statement for the UIKit framework. Add another import statement for the AVFoundation framework so that the code looks like this:

#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>

Just as importing UIKit lets you use things like UIButton and UILabel, importing AVFoundation lets you use the very useful AVAudioPlayer class.

Next, you’ll need an instance variable for each of the three sounds. Add a line for each instance variable as shown below:

@interface ViewController : UIViewController<UIAlertViewDelegate> {
    IBOutlet UILabel *scoreLabel;
    IBOutlet UILabel *timerLabel;
 
    NSInteger count;
    NSInteger seconds;
    NSTimer *timer;
 
    // Add these AVAudioPlayer objects!
    AVAudioPlayer *buttonBeep;
    AVAudioPlayer *secondBeep;
    AVAudioPlayer *backgroundMusic;
}

That’s it for the header file! Time to get some sound playing. Open ViewController.m, and add this new helper method above the viewDidLoad method:

- (AVAudioPlayer *)setupAudioPlayerWithFile:(NSString *)file type:(NSString *)type
{
  // 1
  NSString *path = [[NSBundle mainBundle] pathForResource:file ofType:type];
  NSURL *url = [NSURL fileURLWithPath:path];
 
  // 2
  NSError *error;
 
  // 3
  AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
 
  // 4
  if (!audioPlayer) {
    NSLog(@"%@",[error description]);
  }
 
  // 5
  return audioPlayer;
}

This method will return an AVAudioPlayer object, and takes two arguments: a file name and type. Let’s look at what it does section by section:

  1. You need to know the full path to the sound file, and [NSBundle mainBundle] will tell you where in the project to look. AVAudioPlayer needs to know the path in the form of a URL, so the full path is converted to URL format.
  2. A NSError object will store an error message if something goes wrong when setting up the AVAudioPlayer. Usually nothing will go wrong — but it’s always good practice to check for errors, just in case!
  3. This is the important call to set up AVAudioPlayer. You’re passing in the URL, and the error will get filled in if something goes wrong.
  4. If something goes wrong, the audioPlayer will be set to nil, which you can check for with the exclamation mark symbol. If that happens, the error will be logged to the console.
  5. If all goes well, the AVAudioPlayer object will be returned!

Now that you have a handy method that will set up AVAudioPlayer objects, it’s time to use it! Add this code to the viewDidLoad method just after setting up the background images:

  buttonBeep = [self setupAudioPlayerWithFile:@"ButtonTap" type:@"wav"];
  secondBeep = [self setupAudioPlayerWithFile:@"SecondBeep" type:@"wav"];
  backgroundMusic = [self setupAudioPlayerWithFile:@"HallOfTheMountainKing" type:@"mp3"];

At this point in viewDidLoad, you’ll have all three sounds ready to be called in your code! :]

The first sound, buttonBeep, should play when the button is pressed. Update the buttonPressed method to play the sound:

- (IBAction)buttonPressed {
  count++;
 
  scoreLabel.text = [NSString stringWithFormat:@"Score\n%i", count];
 
  // add this line
  [buttonBeep play];
}

Run the project to test it out. You should hear a nice beep when you tap the button!

There are two other sounds to add. The secondBeep sound should be played every second when the timer ticks down. Add the call to play that sound in subtractTime method:

- (void)subtractTime {
  seconds--;
  timerLabel.text = [NSString stringWithFormat:@"Time: %i",seconds];
 
  // add this line
  [secondBeep play];
 
  // ...the rest of the method continues here...

Run the project again to test it out. You should hear a different beep as the number of seconds counts down. You’re almost done!

The final step is to add the background music. To play the music every time a new game is started, add the play code to the setupGame method. Add this code to the bottom of setupGame:

  [backgroundMusic setVolume:0.3];
  [backgroundMusic play];

You can adjust the volume of the background music so the beeps can still be heard. The setVolume method will take a number from 0 (off) to 1.0 (full volume); 0.3 is a good starting point for the background music.

Now run the project — and experience the glory of your fully featured app!

Feels good to be awesome, doesn’t it? :]

Final Thoughts

Congratulations! You have just made your first iPhone app, and taken it from having very basic functionality, to being a polished looking and sounding app.

There are lots of things that you can modify in your app, such as adding or changing some of the graphics, adding different difficulty levels, or even modifying the sound effects – the sky is the limit!

From here, you might want to try some of the other tutorials on this site. We have a ton to choose from – whatever your interests may be.

In the meantime, if you have any questions or comments, please join the forum discussion below!

Mike Jaoudi
Mike Jaoudi

Mike Jaoudi is a Computer Science major at New York University. For the past couple of years, Mike also worked as an Instructor and Curriculum Developer at iD Tech Camps for iOS App Design and iOS Game Design. While at iD Tech, Mike discovered an enjoyment for teaching, as he’s found that “when you teach you learn twice”. He is a fan of the BBC show “Sherlock” and NCAA fencer. Check out his multiplayer snake game, Snakez. You can find Mike on Twitter .

User Comments

43 Comments

[ 1 , 2 , 3 ]
  • Thanks for the tutorials; have been learning a lot about IOS development but am stuck. I get the following error,

    2014-03-12 08:09:10.615 Tap Me[3389:70b] Application windows are expected to have a root view controller at the end of application launch

    Sometimes, I quit Xcode and then relaunch it and it works once or twice before getting the error again. Right, now it does not want to go. Please help.
    jorge_flores55
  • Yes, this article relatives set the bottn,imagvewe, label background and the sound play!
    guoguo
  • Hello! Ive followed every step in the tutorial and thought everything would work as a flow. But when i finally press simulate i get a wrong message .

    Can anyone help me out? im lost :(
    I use xcode Version 5.1 (5B130a) and IOS 7.

    This is what happens: #import

    #import "testAppDelegate.h"

    int main(int argc, char * argv[])
    {
    @autoreleasepool {
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([testAppDelegate class]));
    }
    }
    maestro
  • Thank you so much for posting a nice tutorial... I am really happy because i have read your 4 parts of iOS app development.
    Really useful....
    chetan.nasit
  • It is awesome tutorial for beginners. :-)
    Archit
  • Amazingly well written tutorial. I'm an upper beginner or maybe a bit higher in iOS and I'm used to the rather abstract and dry writing style you see in the major books (BNR etc). Mike Jaoudi's writing is incredibly accessible and really makes you want to follow the tutorial to the end. Also, knowing a fair chunk of C and Objective-C, I was able to make sense of all the code. I learned a ton!! Anyway, I've been focusing 100% on just core data management and table view nav stuff etc., so this is my first look at making an app look great. Can't wait to learn more. Thanks for a kick-ass tutorial!
    tokyomike55
  • Thank you so much for this amazing tutorial, thank you for your effort, it is very nice, helpful and not boring.

    I have some weird observation, [backgroundMusic play]; doesn't work when i put it in setupGame method, I don't know why, i tried several times, when i put it ANYWHERE else it works, viewDidLoad, buttonPressed, subtractTime, all works, except for setupGame, it makes no sense at all to me, why is this happening?

    Thank you again.
    Moodyxo
  • Thank you, Mike. The app was very fun and educational to build.
    JonBoy64
  • To Moodyxo:

    Make sure you have added the following three lines of code BEFORE the line [self setupGame]; line.

    If you add them AFTER the [self setupGame]; line, the button sounds will work, but the music will NOT play.

    buttonBeep = [self setupAudioPlayerWithFile:@"ButtonTap" type:@"wav"];
    secondBeep = [self setupAudioPlayerWithFile:@"SecondBeep" type:@"wav"];
    backgroundMusic = [self setupAudioPlayerWithFile:@"HallOfTheMountainKing" type:@"mp3"];

    In other words, make sure that [self setupGame]; is the LAST line of code in your viewDidLoad method.

    I hope that may help you.
    JonBoy64
  • Question! When I load the image to my app, there is a square whitespace around my image as if it had a background itself in the image instead of just being circular. Is this happening with a lot of people? Is there a way I can fix it in xcode? I am sure I could just edit it myself in an image editor if I wanted to...
    bwighthunter
  • Im having some serious constraint issues.... not sure if this is due to the update last month. I can't even get passed the 'Image Frenzy!' section.
    caraballoj
  • @bwighthunter go to storyboard, click on the image, then go to attribute inspector and scroll down to the 'view' section. Make sure your background is set to default. hope this helps!
    caraballoj
  • Re: constraint issue...... once I switched the ios simulator to iPhone 5 everything worked out perfectly.
    caraballoj
[ 1 , 2 , 3 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Vote for Our Next Tutorial!

Every week, we alternate between Gaming and Non-Gaming tutorial votes. This week: Non-Gaming!

    Loading ... Loading ...

Last week's winner: How To Make a Tower Defense Game with Swift.

Suggest a Tutorial - Past Results

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in December: The Great CALayer Tour

Sign Up - December

Our Books

Our Team

Tutorial Team

  • Pietro Rea
  • Kyle Richter

... 59 total!

Update Team

... 13 total!

Editorial Team

  • John Clem
  • Matt Galloway

... 17 total!

Code Team

  • Orta Therox

... 3 total!

Subject Matter Experts

  • Richard Casey

... 4 total!