How To Make a Letter / Word Game with UIKit: Part 1/3

Marin Todorov
Learn how to create a fun anagrams game!

Learn how to create a fun anagrams game!

Update 11/5: We have updated this post for iOS 8 and Swift – check out the updated version!

Are you a seasoned iOS developer who is thinking of creating an iOS game, but have no experience programming games? Maybe learning OpenGL ES makes you uncomfortable, or maybe you’ve just never crossed paths with Cocos2d for iPhone?

Well, good news! UIKit is a power tool for game developers. Its high-end frameworks provide you with the means to create all of your game logic, from creating animations (including frame-animation) to playing audio and video.

This 3-part tutorial series will guide you through the process of building a board game for the iPad, where you create words with letters. You’ll also learn about best practices for creating solid, modular iOS apps. And as a bonus, you’ll get a crash course in audio-visual effects with UIKit!

This tutorial series is designed for readers who are already somewhat familiar with Objective-C, and thus won’t go over details like primitive types or syntax. If your Obj-C knowledge is rusty, I recommend checking out our Objective-C Crash Course.

Click through to get your game on!

Getting Started: Introducing Anagrams

The letter / word game with UIKit you will be making in this tutorial is about anagrams.

An anagram is a word or phrase formed by rearranging the letters of another word or phrase. For example, the word cinema can be rearranged into the word iceman. Your game will challenge the player to create anagrams from words and phrases you provide.

The finished game will look something like this:

As you can see, there is a row of letter tiles on the lower end of the screen that makes up the initial phrase. In the screenshot above it is the word admirer, minus the M tile, which the player has dragged to the upper row.

The player’s job is to drag the letters from admirer to form a different word, using the same letters.

Can you guess what it is? I’ve already given you a hint… it starts with an M!

Solution Inside: Answer SelectShow

So anyway, this is the game you will be making. As you build this game, you will learn about the following foundational topics:

  • Proper model-view-controller (MVC) game structure.
  • Loading level configuration from config files.
  • Organizing your game controller logic.
  • Writing a simple audio controller for UIKit with AV Foundation.
  • Using QuartzCore for static visual effects.
  • Using UIAnimation to smooth in-game movements.
  • Using UIKit particles to add visual effects.
  • Building separate HUD and game layers.
  • Using game fonts to improve the look and polish.
  • Upgrading default UIKit components to game-like components.
  • Also included: discussion of the software tools I use along the way when I’m creating a game. :]

Oh, and there will be explosions. :] Let’s dig in!

The Starter Project

To get you straight to the good stuff – using UIKit to build an iOS game – this tutorial provides a ZIP file that includes a starter XCode project and all game assets (images, fonts, audio and config files).

Download the Anagrams Part 1 starter file, unzip it and open it up in Xcode.

Have a look at what’s bundled in the starter project. The project file browser on the left should look something like this:

Here’s a quick summary of what you see:

  • config.h: Your code configs in one place.
  • Levels: A folder with three .plist files to define the game’s three difficulty levels.
  • Classes/models: This is where you’ll add your data models.
  • Classes/views: This is where you’ll create your custom view classes.
  • Classes/controllers: This is where you’ll create your controllers.
  • Assets/Fonts: A custom TTF font for the game HUD.
  • Assets/Particles: There is a single PNG file to use for particle effects.
  • Assets/Audio: Creative Commons-licensed audio files.
  • Assets/Images: These are all the images you’ll use to create the game.
  • MainStoryboard.storyboard: Open up the storyboard and you’ll see there’s only one screen with the background set to a faint woodgrain image, which is included in the project assets.
  • credits.txt: Includes links to the sources of all the assets used in the project.

Poke around, play the sounds and browse the images. Then run the project.

It’s much better than an empty app window, isn’t it?

In this first part of the series, your goal is to display the initial anagram and target boxes on the screen. In future tutorials, you’ll add the ability to drag the tiles, and the gameplay logic.

There’s a lot involved to get this first part working. But don’t worry, you’ll break down this epic task into 8 small steps:

  1. Load the level config file.
  2. Create a game controller class.
  3. Create an onscreen view.
  4. Create a tile view.
  5. Deal the tiles on the screen.
  6. Add the letters.
  7. Slightly rotate the letters randomly.
  8. Add the targets.

Let’s go over these one step at a time.

1) Load the Level Config File

Select the level1.plist file in the project file browser. Have a look what’s inside:

There are three top-level keys:

  • pointsPerTile: the points awarded for each correctly-placed tile.
  • timeToSolve: The number of seconds the player will have to solve the puzzle.
  • anagrams: A list of anagrams, each anagram containing two items, an initial and a final phrase.

Don’t bother for the moment with the first two and focus on anagrams. It’s an array of array-elements and each element has two string elements. You’re now going to build a data model to load this configuration in Objective-C.

Create a new Objective-C class file in the Anagrams/Classes/models folder. Call the new class Level and make it a subclass of NSObject.

Open up Level.h and in between the @interface and @end lines, add the following properties and method for your Level class:

//properties stored in a .plist file
@property (assign, nonatomic) int pointsPerTile;
@property (assign, nonatomic) int timeToSolve;
@property (strong, nonatomic) NSArray* anagrams;

//factory method to load a .plist file and initialize the model
+(instancetype)levelWithNum:(int)levelNum;

The properties match the structure of the .plist file, so you are simply going to load the data and populate the properties. You also declare a factory method to produce Level class instances for a given difficulty level. These will range from 1 to 3, going from easiest to most difficult.

Now add the code to power-up the Level data model class. Inside the class implementation in Level.m, add the method body of the factory method to create Level instances:

+(instancetype)levelWithNum:(int)levelNum;
{
    //1 find .plist file for this level
    NSString* fileName = [NSString stringWithFormat:@"level%i.plist", levelNum];
    NSString* levelPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];

    //2 load .plist file
    NSDictionary* levelDict = [NSDictionary dictionaryWithContentsOfFile:levelPath];

    //3 validation
    NSAssert(levelDict, @"level config file not found");
    
    //4 create Level instance
    Level* l = [[Level alloc] init];

    //5 initialize the object from the dictionary
    l.pointsPerTile = [levelDict[@"pointsPerTile"] intValue];
    l.anagrams = levelDict[@"anagrams"];
    l.timeToSolve = [levelDict[@"timeToSolve"] intValue];

    return l;
}

There are four small sections in the method body – four small steps towards the goal of the method, if you will:

  1. Determine the path to the appropriate level config file (level1.plist, level2.plist, etc), based on the levelNum value passed into the method.
  2. Load the file’s contents into an NSDictionary. Fortunately, the NSDictionary class knows how to parse .plist files!
  3. Validate that there’s something loaded in levelDict. I use NSAssert often for errors that should never happen in a shipped app. (I.e., if they EVER happen during development I want the app to stop and crash, so I can fix it before doing anything else.)
  4. Create a new instance of Level.
  5. Copy the values from the NSDictionary to the Level object’s properties.

Now let’s give the class a try and validate your achievement of the first step towards the goal!

Open up ViewController.m and add at the top:

#import "Level.h"

And at the end of viewDidLoad:, add:

Level* level1 = [Level levelWithNum:1];
NSLog(@"anagrams: %@", level1.anagrams);

In the Xcode console you will see the list of anagrams for Level 1. Nice!

Now you can remove the NSLog line from viewDidLoad to keep the console tidy.

That’s one task knocked down. Ready to move ahead to the next one?

2) Create a Game Controller Class

Often people who are not experienced with MVC before jumping into iOS development confuse a controller and a UIViewController.

  • A controller is simply a class that focuses on some part of your app’s logic, separate from your app’s data and the classes used to display that data.
  • A class that subclasses UIViewController is just a controller managing the presentation of a particular UIView. (And UIViews are used to display something on the screen.) You can read up more on the MVC pattern here.

For this tutorial, you are going to create a controller class to manages your game’s logic. Let’s try this out.

Create a new Objective-C class file in the Anagrams/Classes/controllers folder. Call the new class GameController and make it a subclass of NSObject.

Open GameController.h and add the following import:

#import "Level.h"

Then add the following to the GameController interface:

//the view to add game elements to
@property (weak, nonatomic) UIView* gameView;

//the current level
@property (strong, nonatomic) Level* level;

//display a new anagram on the screen
-(void)dealRandomAnagram;

I bet you’ve already spotted the building blocks of your current goal:

  • gameView: The view you’ll use to display game elements on the screen.
  • level: The Level object that stores the anagrams and other settings for the current game level.
  • dealRandomAnagram: The method you will call to display the current anagram on the screen.

The code so far is pretty simple. Switch to GameController.m and add this placeholder version of dealRandomAnagram:

//fetches a random anagram, deals the letter tiles and creates the targets
-(void)dealRandomAnagram
{
    //1
    NSAssert(self.level.anagrams, @"no level loaded");
    
    //2 random anagram
    int randomIndex = arc4random()%[self.level.anagrams count];
    NSArray* anaPair = self.level.anagrams[ randomIndex ];
    
    //3
    NSString* anagram1 = anaPair[0];
    NSString* anagram2 = anaPair[1];
    
    //4
    int ana1len = [anagram1 length];
    int ana2len = [anagram2 length];
    
    //5
    NSLog(@"phrase1[%i]: %@", ana1len, anagram1);
    NSLog(@"phrase2[%i]: %@", ana2len, anagram2);    
}

Here’s what happening in the code above:

  1. You check to make sure this method is only called after the level property is set and that its Level object contains anagrams.
  2. You generate a random index into the anagram list, then grab the anagram at this index.
  3. You store the two phrases into anagram1 and anagram2.
  4. Then you store the number of characters in each phrase into ana1len and ana2len. You’ll do more with this information later.
  5. Finally, you print the phrases to the console. This will suffice for testing.

Your app is already set up to load the storyboard MainStoryboard.storyboard at start up. This storyboard creates a single screen using the ViewController class.

Since your game will only have one screen, you will add all initialization inside the init method of ViewController. For games that include more than one view controller, you may want to do your initialization someplace else, such as the AppDelegate class.

Open up ViewController.m and at the top, add an import for your new class:

#import "GameController.h"

Replace the private class interface with the following:

@interface ViewController ()
@property (strong, nonatomic) GameController* controller;
@end

Finally, in the implementation add a new initializer method:

-(instancetype)initWithCoder:(NSCoder *)decoder
{
    self = [super initWithCoder:decoder];
    if (self != nil) {
        //create the game controller
        self.controller = [[GameController alloc] init];
    }
    return self;
}

The above method initWithCoder: is called automatically when the app initializes itself from its storyboard.

There it is! You’ve created a GameController class and you have an instance of it in your ViewController implementation, ready to go off and spill some letter tiles on the screen at any moment. :]

3) Create an Onscreen View

The next step is also quite easy: you need to create a view on the screen and connect your GameController to it.

Open up config.h and look at the first two defines inside the file: kScreenWidth and kScreenHeight. These are just two shortcuts, which also take care of the swapped dimensions of the screen in Landscape mode. You are going to use these defines to create your game view.

Switch back to ViewController.m and add the following code at the end of viewDidLoad::

//add one layer for all game elements
UIView* gameLayer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[self.view addSubview: gameLayer];

self.controller.gameView = gameLayer;

In the above code chunk, you create a new view with the dimensions of the screen and add it to the ViewController‘s view. Then you assign it to the gameView property of your GameController instance. This way the GameController will use this view for all game elements (except for the HUD – you’ll take care of that later).

Having now created your model and controller classes, you’ve come to the point where you need your first custom view class for the Anagrams game to display the tiles.

4) Create a Tile View

In this section, you are going to build a view to show tiles like these:

For now, you’ll focus on just creating the tile squares with the background image. Later, you’ll add the letters on top.

Inside the file group Anagram/Classes/views, create a new class called TileView and make it a subclass of UIImageView. This is already a good start – the UIImageView class provides you with the means to show an image, so you’ll only need to add a label on top of the image later on.

Open TileView.h and add the following to the interface:

@property (strong, nonatomic, readonly) NSString* letter;
@property (assign, nonatomic) BOOL isMatched;

-(instancetype)initWithLetter:(NSString*)letter andSideLength:(float)sideLength;

Here’s what you’ve just created:

  • letter: A property that will hold the letter assigned to the tile.
  • isMatched: A property that will hold a Boolean indicating whether this tile has already been successfully “matched” to a target on the top of the screen.
  • initWithLetter:andSideLength: a custom init method to set up an instance of the class with a given letter and tile size.

Why do you need to set the size of the tile, you might ask?

Look at the these two setups:

The first example shows a short word that requires large tiles to fill the screen. The latter example shows a phrase that requires smaller tiles because it has almost double the tile count. You can see why the tile view needs to be resize-able so that you can show different size tile depending on how many tiles you need on the screen at a time.

Your controller will calculate the phrase length, divide the screen width by the number of characters and decide on a tile size. You’ll implement that soon, but first, get cracking on the code for the tiles.

Switch to TileView.m and replace the contents with this basic class skeleton:

#import "TileView.h"
#import "config.h"

@implementation TileView

//1
- (id)initWithFrame:(CGRect)frame
{
    NSAssert(NO, @"Use initWithLetter:andSideLength instead");
    return nil;
}

//2 create new tile for a given letter
-(instancetype)initWithLetter:(NSString*)letter andSideLength:(float)sideLength
{
    //the tile background
    UIImage* img = [UIImage imageNamed:@"tile.png"];
    
    //create a new object
    self = [super initWithImage:img];
    
    if (self != nil) {
        
        //3 resize the tile
        float scale = sideLength/img.size.width;
        self.frame = CGRectMake(0,0,img.size.width*scale, img.size.height*scale);
        
        //more initialization here
    }
    
    return self;
}

@end

This is a long chunk of code, but if you look closely, you’ll see it doesn’t do much: it’s all about defining placeholders so you can build up the tile functionality later on. There are a few different sections in the code, so let’s go by the numbers:

  1. You override initWithFrame:. Since you have a custom initialization method, you don’t ever want to call initWithFrame:. Instead, you add an NSAssert that will fail every single time. This means there is less of a chance that you will ever create a tile that isn’t properly initialized.

    In such a small app, this is just a way to protect yourself from yourself. But in a larger app, or when working as part of a team, this sort of precaution can help reduce errors by keeping anyone from calling this method. (In order to really protect yourself from yourself, you would want to create similar implementations for each of the following methods: init, initWithCoder:, initWithImage: and initWithImage:highlightedImage:. You can do those later by yourself.)

  2. initWithLetter:andSideLength: is your custom method that will initialize tile instances. It loads the tile.png image and makes an instance of TileView by calling initWithImage: on the parent class (UIImageView in this case).
  3. You then calculate by how much you need to scale the default tile so that you get a size that will fit your board for the given word or phrase. To do that, you just adjust the frame of the TileView. UIImageView resizes its image automatically to match its frame size.

Sweet! That code is already enough to show tiles containing just the default empty tile.png image.

Now is a good time to implement more functionality of the game controller, so you can at least see some empty tiles lining up on the screen.

5) Dealing the Tiles on the Screen

Switch back to GameController.m – your dealRandomAnagram method already loads the data you need, but it still does not do much. Time to fix that!

Add the following imports with the others at the top of GameController.m:

#import "config.h"
#import "TileView.h"

Now you’ll have access to your TileView class and any shared settings you store in config.h.

Add the following private variables to the @implementation section:

@implementation GameController
{
    //tile lists
    NSMutableArray* _tiles;
    NSMutableArray* _targets;
}

So what’s up with these variables?

  • _tiles is an array to hold the TileViews that will display the tiles at the bottom of the screen. These tiles contain the initial phrase of the anagram.
  • _targets is an array to hold the target views that will display the spaces at the top of the screen where the player has to drag tiles to form the second phrase of the anagram.

Before you continue, you also need a constant for the space between the tiles. Jump over to config.h and add the following define:

#define kTileMargin 20

Some of you might already be protesting, “Can’t I just use 20 where I need it and be done?” It’s a good point, but there is a reason for predefining in the config file.

My experience shows that you will have to tweak any numbers you have in your code when it comes time to fine-tune the gameplay. So the best practice is to externalize all numbers to a config file and give those variables meaningful names. This will pay off especially well if you are working in a team and you need to hand the game to a game designer for fine-tuning. :]

OK, back in GameController.m, get ready to add some code to the end of dealRandomAnagram. You have the number of characters in both phrases, so it’s easy to calculate what size tiles you need. Add this code at the end of the method:

//calculate the tile size
float tileSide = ceilf( kScreenWidth*0.9 / (float)MAX(ana1len, ana2len) ) - kTileMargin;

You take 90% of the width of the screen and divide it by the number of characters. You use the phrase that has more characters – remember, the number of spaces can vary! You decrease the result by kTileMargin to account for space between tiles.

Next find the initial x position of the first tile. It will depend on the length of the word and the tile size you just calculated. Once again, add the following at the end of dealRandomAnagram:

//get the left margin for first tile
float xOffset = (kScreenWidth - MAX(ana1len, ana2len) * (tileSide + kTileMargin))/2;

//adjust for tile center (instead of the tile's origin)
xOffset += tileSide/2;

You take the screen width, subtract the calculated width of the word tiles and find where the first tile should be positioned on the screen. Wouldn’t it be nice if there were a simpler way to “center” the tiles!

LeSighHisLeg

Continuing in the same method, add the following code to display the tiles:

//1 initialize tile list
_tiles = [NSMutableArray arrayWithCapacity: ana1len];

//2 create tiles
for (int i=0;i<ana1len;i++) {
    NSString* letter = [anagram1 substringWithRange:NSMakeRange(i, 1)];
    
    //3
    if (![letter isEqualToString:@" "]) {
        TileView* tile = [[TileView alloc] initWithLetter:letter andSideLength:tileSide];
        tile.center = CGPointMake(xOffset + i*(tileSide + kTileMargin), kScreenHeight/4*3);

        //4
        [self.gameView addSubview:tile];
        [_tiles addObject: tile];
    }
}

Here's what's happening above:

  1. First you initialize a fresh copy of your _tiles mutable array and ensure it is large enough to hold all of the letters in the initial phrase. It's important to start with a clear array here because this method will be called multiple times during a run of your app. Since spaces are skipped, if you didn't clear out the array then, where there should be spaces, there might instead be old tiles left over from a previous phrase.
  2. You then loop through the phrase and create a new NSString for each character. This is stored as letter.
  3. You check each letter and if it is not a space, it's a go! You create a new tile and position it using xOffset, tileSide and kTileMargin. Here you are calculating the position based on that letter's position in the phrase, which is represented by i. So if i is 5, this math will figure out how much space five tiles take up and add the new tile to the right of that space.

    You position the tiles vertically at 3/4ths of the screen height.
  4. Finally, you add the tile to the gameView and to the _tiles array. The first actually shows the tile on the screen, while the latter helps you loop faster over the tiles later on.

Alrighty! Time for the final touch – you need to load the level data and call dealRandomAnagram. Switch to ViewController.m and add these two lines at the end of viewDidLoad::

self.controller.level = level1;
[self.controller dealRandomAnagram];

Remember that you already have a Level object – you created it at the top of this method – so you just pass it on to your GameController and call dealRandomAnagram.

It's been a long time in coming, but build and run to check out the result! Your Simulator should look something like this:

Awesome, it works! It's alive!!!

Note: Your view probably won't look exactly like this because the app is choosing a random anagram each time it runs. Run it a few times to see how the app sizes the tiles differently for different phrases.

You are closing in on the finish line for the first part of this tutorial series.

6) Add the Letters

Switch to TileView.m. This is where you're going to add letters to those tiles. Find the comment in initWithLetter:andSideLength saying "//more initialization here" and just below it, add the following code:

//add a letter on top
UILabel* lblChar = [[UILabel alloc] initWithFrame:self.bounds];
lblChar.textAlignment = NSTextAlignmentCenter;
lblChar.textColor = [UIColor whiteColor];
lblChar.backgroundColor = [UIColor clearColor];
lblChar.text = [letter uppercaseString];
lblChar.font = [UIFont fontWithName:@"Verdana-Bold" size:78.0*scale];
[self addSubview: lblChar];

The above code creates a new UILabel and adds it to the tile view. Because you want the letter to be as easy to read as possible, you create the label to be as large as the tile itself. You then align the letter to show up in the center of the tile.

You ensure the letter is uppercase by calling [letter uppercaseString]. Using capital letters will ease the game play, as they are easier to recognize, specifically by children. Always think about your game's target audience!

Finally you reuse the scale factor you calculated earlier. For the full-size tile a Verdana font with char size of 78pts fits well, but for other sizes you need to scale the font size accordingly.

Add these last few lines of initialization to the end of initWithLetter:andSideLength:

//begin in unmatched state
self.isMatched = NO;

//save the letter
_letter = letter;

The above code sets the tile's isMatched flag to NO, indicating this tile has not yet been matched to a target at the top of the screen. It also saves the letter to the tile's letter property.

Build and run the project. As I said, you're now moving fast towards your goal. Good job! :]

Once again, your screen probably won't look just like this one. But that just means your game controller is doing what's it's supposed to do and dealing a random anagram. Nice!

Have another look at the image above. Right now the tiles look like they were put on the board by a pedantic robot – they are perfectly aligned. Now comes the time to add a pinch of randomization!

7) Slightly Rotate the Letters Randomly

Randomization is one of the keys to building a successful computer game. You want to randomize your game as much as possible. For example, no player enjoys enemies that all do the same thing every time the game runs.

In the case of the Anagrams game, you can randomize the placement of each tile so that they look more natural on the game board, as if a human put them down without trying too hard to align them properly:

In order to accomplish this randomization, add the following method to the interface in TileView.h:

-(void)randomize;

Inside TileView.m, add the following implementation of randomize:

-(void)randomize
{
    //1
    //set random rotation of the tile
    //anywhere between -0.2 and 0.3 radians
    float rotation = randomf(0,50) / (float)100 - 0.2;
    self.transform = CGAffineTransformMakeRotation( rotation );

    //2
    //move randomly upwards
    int yOffset = (arc4random() % 10) - 10;
    self.center = CGPointMake(self.center.x, self.center.y + yOffset);
}

This code does two things:

  1. It generates a random angle between -0.2 and 0.3 radians (using the helper function randomf defined in config.h). Then it calls CGAffineTransformMakeRotation to create a new transform for the tile view that will rotate it around its center by that random angle.
  2. It generates a random value between -10 and 0 and moves the tile up by that offset.

Note: You might want to replace all of these numbers with constants you define in config.h so you can fine-tune the tile appearance more easily.

Now you just need to call this method. Inside GameController.m, edit dealRandomAnagram by adding the following line of code just after the line that begins with tile.center = ...:

[tile randomize];

And that's all it takes! Hit run again to see the randomized tiles on the board:

Using what you've already achieved, you can also quickly add the targets to the game board. Let's do that before wrapping up this part of the tutorial.

8) Add the Targets

The target views are pretty similar to the tile views. They also subclass UIImageView and show a default image, and they also are assigned a letter. The difference is that they don't show a label with that letter and can't be dragged around. This means the code you need for the targets is similar to what you did before – but simpler!

Create a new class in Anagrams/Classes/views called TargetView and make it a subclass of UIImageView.

Open up the freshly created TargetView.h and inside the interface declaration add the following two properties and method:

@property (strong, nonatomic, readonly) NSString* letter;
@property (assign, nonatomic) BOOL isMatched;

-(instancetype)initWithLetter:(NSString*)letter andSideLength:(float)sideLength;

Yes, these are exactly the same properties and method name you used for the TileView! As you see, now you can move quickly. You already know what the above are for, so you can move straight to the implementation.

Switch to TargetView.m and delete the provided methods. Replace them with:

- (id)initWithFrame:(CGRect)frame
{
    NSAssert(NO, @"Use initWithLetter:andSideLength instead");
    return nil;
}

//create a new target, store what letter should it match to
-(instancetype)initWithLetter:(NSString*)letter andSideLength:(float)sideLength
{
    UIImage* img = [UIImage imageNamed:@"slot"];
    self = [super initWithImage: img];
    
    if (self != nil) {
        //initialization
        self.isMatched = NO;

        float scale = sideLength/img.size.width;
        self.frame = CGRectMake(0,0,img.size.width*scale, img.size.height*scale);
        
        _letter = letter;
    }
    return self;
}

Once again, this is the same code you started with in TileView.m. You resize the image according to the provided side length and assign the letter – that's all.

Now to display the targets on the screen. Open up GameController.m and add the following import:

#import "TargetView.h"

Then add the following code inside dealRandomAnagram, just before the comment that reads "//initialize tile list":

// initialize target list
_targets = [NSMutableArray arrayWithCapacity: ana2len];

// create targets
for (int i=0;i<ana2len;i++) {
    NSString* letter = [anagram2 substringWithRange:NSMakeRange(i, 1)];

    if (![letter isEqualToString:@" "]) {
        TargetView* target = [[TargetView alloc] initWithLetter:letter andSideLength:tileSide];
        target.center = CGPointMake(xOffset + i*(tileSide + kTileMargin), kScreenHeight/4);

        [self.gameView addSubview:target];
        [_targets addObject: target];
    }
}

This is almost the same code you used to deal the tiles on the board, except this time you grab the letters from the phrase stored in anagram2 and you don't randomize their position. You want the target views to look nice and neat, after all!

Why does the target initialization have to come before the tile initialization? Subviews are drawn in the order they are added to their parent view, so adding the target views first ensures that they will appear behind, or below, the tiles.

If you did want to add the target views after the tile views, there are other ways to get them behind the tiles, such as UIView's sendSubviewToBack: method. But adding them first is the most efficient solution.

Build and run again to see that your board is already close to being finished!

Waaaaaait a secoooond!!!

These two phrases don't match. Below you have three tiles followed by four tiles, while the targets at the top are four tiles following by three tiles!

Don't worry, you should actually expect this behavior. After all, the anagrams consist of two phrases which might each have different word counts. They just need to have an equal number of total characters (not including white spaces). So it's all good!

Where to Go from Here?

Click here to download the full source code for the project up to this point.

Congrats, you've achieved a lot today! Let's see what you've done so far:

  • You've created a model to load the level configuration.
  • You've connected the game controller, game view and model.
  • You have two custom views, one for tiles and one for targets.
  • You've finished the initial board setup.

You now have a sound foundation on which to build. Stay tuned for Part 2 of the series, where you'll begin interacting with the player and building up the gameplay.

In the meantime, if you have any questions or comments about what you've done so far, drop by the forums to contribute your two tiles' worth. :]

Marin Todorov

Part of: Realm and raywenderlich.com. Author of books and apps. More: <a href="http://www.underplot.com">www.underplot.com</a>

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 43 total!

Android Team

... 14 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 12 total!

Resident Authors Team

... 15 total!