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

In this second part of the tutorial series, you’ll aim for developing a fully playable version of the game. When you’re finished, the user will be able to drag the tiles and drop them on the correct targets, where they will “stick” to the spot. By Caroline Begbie.

Leave a rating/review
Save for later
Share

Update note: This tutorial was updated for Swift and iOS 8 by Caroline Begbie. Original series by Tutorial Team member Marin Todorov.

Update 04/12/15: Updated for Xcode 6.3 and Swift 1.2

Welcome back to our 3-part tutorial series that shows you how to make a letter / word game with UIKit – in this case, a game of anagrams!

If you successfully followed through the first part of this series, you should now have your game board showing up onscreen. So far, you’ve learned how to kick-start your UIKit game, how to plan your controllers and how to connect your views, and have also started implementing the game elements.

Now you’ll take on the gameplay itself and grant the player some special powers, like dragging the tiles around and dropping them onto the targets at the top of the screen. A heads-up-display with a timer and score ticker will keep the player’s ego in check.

Time to grab your UIKit and get back to work!

Getting Started: Your Epic Goal

When you run the completed project from Part 1 of this tutorial, you should see something like this:

Part 2 Start

In this second part of the tutorial series, you’ll aim for developing a fully playable version of the game. When you’re finished, the player will be able to drag the tiles and drop them on the correct targets, where they will “stick” to the spot. When all tiles have been placed on the correct targets, the player will win that puzzle.

There will also be a timer – the player will have a limited amount of time to finish each puzzle – and a score display that will increase and decrease in real time, according to the player’s actions.

You’ll achieve these epic goals by taking on these smaller steps:

  • Make the tiles draggable.
  • Make the tiles notify the game controller when they are dropped somewhere.
  • Implement handling of tile drops in the game controller.
  • Check if the player has won after each successful tile placement.
  • Create a separate view layer to contain elements like score readouts and menus. It’s usually best to keep these arranged in their own view, rather than including them in the same view as the actual gameplay elements.
  • Add a level timer and a player score. This is a game, after all!

All right! You’ve been debriefed. Now back to the code!

Drag Those Tiles

You might already be familiar with how to handle touch events with UIKit. If you are, there’s nothing different about handling touches in a UIKit game. You just need to implement the touch delegate methods in TileView. Before your tiles can receive touch events, however, you need to enable them.

Inside TileView.swift, add the following line to the end of init(letter:sideLength:):

self.userInteractionEnabled = true

This instructs iOS to send touch events to this object. By default, this is set to false for a UIImageView (which this class derives from), so you have to set it to true manually.

Next, you need to add the dragging code. To accomplish this, you are going to take the following strategy:

  • When the player touches down on a tile, figure out the offset within the tile of where their finger lies.
  • When the player drags, you’ll set the center of the tile to the new position of their finger – except you’ll shift the tile by the offset you computed earlier to account for where the player’s finger is within the tile.

Let’s try this out. Still in TileView.swift, add new properties to the top of the class:

private var xOffset: CGFloat = 0.0
private var yOffset: CGFloat = 0.0

You’ll use xOffset and yOffset to keep track of the distance between the center of the tile and the initial placement of the player’s finger when the touch begins.

To handle dragging the tiles, add the following touch handling methods to the TileView class:

//1
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
  if let touch = touches.first as? UITouch {
    let point = touch.locationInView(self.superview)
    xOffset = point.x - self.center.x
    yOffset = point.y - self.center.y
  }
}
  
//2
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
  if let touch = touches.first as? UITouch {
    let point = touch.locationInView(self.superview)
    self.center = CGPointMake(point.x - xOffset, point.y - yOffset)
  }
}
  
//3
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
  self.touchesMoved(touches, withEvent: event)
}

Here’s what’s going on in the above methods:

  1. When a touch is detected, you fetch its location within the tile’s superview (that is, the view that contains the tile). You calculate and store the distance from the touch to the tile’s center.
  2. When the player moves their finger, you move the tile to that location, but you adjust the location by the offsets you stored in xOffset and yOffset. This keeps the tile from centering itself under the player’s finger as soon as they start moving it – you want it to feel like the player is dragging the tile by a particular point.
  3. When the player lifts their finger, you make one last call to touchesMoved(touches:withEvent:) to make sure the tile’s position is set to the touch’s final location. You could have just typed the same code here as in touchesMoved(touches:withEvent:), but it’s better to avoid repeating code where possible to make maintenance easier.

All right! Build and run the project and have fun dragging the game tiles around.

Moving the Tiles

Cool – you’re already one step closer to your goal.

Hey, Game Controller! A Tile Is Dropping!

In order to make the game controller accept notifications from tiles when they are dropped somewhere on the board, you’ll make the game controller a delegate to all tiles. The tiles will then invoke a method on their delegate when the player drops them. Therefore, get ready to get some experience with the delegation pattern!

At the top of the TileView.swift file (after the import lines), declare the protocol with a single method:

protocol TileDragDelegateProtocol {
  func tileView(tileView: TileView, didDragToPoint: CGPoint)
}

TileDragDelegateProtocol requires one method – the one that handles a finished drag and drop operation.

Now inside the class, add a property to store the delegate:

var dragDelegate: TileDragDelegateProtocol?

All right! That should be enough declarations – time to add the code. Still in TileView.swift and at the end of touchesEnded(touches:withEvent:) add:

dragDelegate?.tileView(self, didDragToPoint: self.center)

Totally easy, right? If the dragDelegate property is not nil, you just call the delegate’s tileView(_:didDragToPoint:) method, passing it a reference to the tile (self) and self.center.

Now you need to make GameController conform to your new protocol and actually do something with this information.

Contributors

Over 300 content creators. Join our team.