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

This 3-part tutorial series will guide you through the process of building a board game for the iPad in Swift, 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! By Caroline Begbie.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

2) Create a Game Controller Class

People who are not experienced with the model-view-controller (MVC) paradigm before jumping into iOS development often 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 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 manage your game’s logic. Let’s try this out.

Create a new Swift file in the Anagrams/Classes/Controllers folder. Call the new file GameController.

Open GameController.swift and add the following:

import UIKit

class GameController {
	var gameView: UIView!
	var level: Level!

	init() {
	}

	func 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’ll call to display the current anagram on the screen.

The variables are declared as implicitly unwrapped optionals, as they should never be nil, but you will not be creating the instances in the GameController’s initializer.

The code so far is pretty simple. Change dealRandomAnagram() to have this placeholder implementation:

func dealRandomAnagram () {
  //1
  assert(level.anagrams.count > 0, "no level loaded")
    
  //2
  let randomIndex = randomNumber(minX:0, maxX:UInt32(level.anagrams.count-1))
  let anagramPair = level.anagrams[randomIndex]
    
  //3
  let anagram1 = anagramPair[0] as! String
  let anagram2 = anagramPair[1] as! String
    
  //4
  let anagram1length = count(anagram1)
  let anagram2length = count(anagram2)
    
  //5
  println("phrase1[\(anagram1length)]: \(anagram1)")
  println("phrase2[\(anagram2length)]: \(anagram2)")
}

This method will fetch a random anagram, deal the letter tiles, and create the targets. Here’s what happening in the code, section by section:

  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. This randomNumber() function is defined in Config.swift.
  3. You store the two phrases into anagram1 and anagram2.
  4. Then you store the number of characters in each phrase into anagram1length and anagram2length. Even though the letters are the same between anagram1 and anagram2, there can be a different number of spaces so the overall number of characters can be different.
  5. Finally, you print the phrases to the console. This will suffice for testing.

Your app is already set up to load the storyboard Main.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 ViewController initializer. 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.swift and add this property inside the ViewController class:

private let controller:GameController

Finally, add a new initializer to the class:

required init(coder aDecoder: NSCoder) {
  controller = GameController()
  super.init(coder: aDecoder)
}

The app will use the init(coder:) initializer automatically when it instantiates a ViewController from the storyboard.

There it is! You’ve created a GameController class and you have an instance of it in your ViewController class, 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.swift and look at the first two constants inside the file: ScreenWidth and ScreenHeight. You are going to use these constants to create your game view.

Switch back to ViewController.swift and add the following code at the end of viewDidLoad():

//add one layer for all game elements
let gameView = UIView(frame: CGRectMake(0, 0, ScreenWidth, ScreenHeight))
self.view.addSubview(gameView)
controller.gameView = gameView

Here, 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. You’ll create a separate view for the heads-up display (HUD) 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 Anagrams/Classes/Views, create a new Swift file called TileView. Add the following to TileView.swift:

import UIKit

//1
class TileView:UIImageView {
  //2
  var letter: Character
  
  //3
  var isMatched: Bool = false
  
  //4 this should never be called
  required init(coder aDecoder:NSCoder) {
    fatalError("use init(letter:, sideLength:")
  }
  
  //5 create a new tile for a given letter
  init(letter:Character, sideLength:CGFloat) {
    self.letter = letter
    
    //the tile background
    let image = UIImage(named: "tile")!
    
    //superclass initializer
    //references to superview's "self" must take place after super.init
    super.init(image:image)
    
    //6 resize the tile
    let scale = sideLength / image.size.width
    self.frame = CGRect(x: 0, y: 0, width: image.size.width * scale, height: image.size.height * scale)
    
    //more initialization here
  }
}

There are a few different sections in the code:

  1. Make the class a subclass of UIImageView. 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.
  2. letter: A property that will hold the letter assigned to the tile.
  3. isMatched: A property, initially set to false, that will hold a Boolean indicating whether this tile has already been successfully matched.
  4. This is UIImageView‘s required initializer, which must be overridden even though you’ll never use it.
  5. init(letter:sideLength:): a custom initializer to set up an instance of the class with a given letter and tile size. It saves the letter to the tile’s variable and loads the tile.png image.
  6. 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.

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

Look at the these two setups:

The top example shows a short word that requires large tiles to fill the screen. The bottom example shows a phrase that requires smaller tiles because it has almost double the tile count. The tile view needs to be resizeable so that you can show different size tile depending on how many tiles there are 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 this code is already enough to show tiles containing just the empty tile.png image.

Contributors

Over 300 content creators. Join our team.