Finishing the game
In this section, we will finally be able to play the game.
Implementing the game logic
After having all the required functions in place, it's now a straightforward task to complete the game. First of all, we add the instance variables to hold the number of the pairs already created, the current score, and the list of selected cards turned up:
private var selectedIndexes = Array<NSIndexPath>() private var numberOfPairs = 0 private var score = 0
Then, we apply the logic when a card is selected:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { if selectedIndexes.count == 2 || selectedIndexes .contains(indexPath) { return } selectedIndexes.append(indexPath) let cell = collectionView.cellForItemAtIndexPath(indexPath) as! CardCell cell.upturn() if selectedIndexes.count < 2 { return } let card1 = deck[selectedIndexes[0].row] let card2 = deck[selectedIndexes[1].row] if card1 == card2 { numberOfPairs++ checkIfFinished() removeCards() } else { score++ turnCardsFaceDown() } }
We first check whether we have touched an already turned-up card or whether we have two cards turned up. If not, we save the index. Then, we check whether we have flipped the first card, and if not, we proceed to check the values of the cards.
The pattern of checking a condition and leaving the current function if the condition is true is called Guard. It helps make the code more readable by avoiding the use of the else
clause and the nesting of curly braces.
We got a pair
As shown in the previous part of the source, we implement the missing actions in a private
extension:
// MARK: Actions private extension MemoryViewController { func checkIfFinished(){ } func removeCards(){ } func turnCardsFaceDown(){ } }
The first one checks whether we have completed all the pairs, and if so, it presents a popup with the score and returns to the main
menu:
func checkIfFinished(){ if numberOfPairs == deck.count/2 { showFinalPopUp() } } func showFinalPopUp() { let alert = UIAlertController(title: "Great!", message: "You won with score: \(score)!", preferredStyle: UIAlertControllerStyle.Alert) alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { action in self.dismissViewControllerAnimated(true, completion: nil)})) self.presentViewController(alert, animated: true, completion: nil) }
Note that in iOS 8, UIAlertController
is slightly different from that in the previous version. In our case, a simple dialog box with an OK button is enough.
If the cards are equal, we need to remove them:
func removeCards(){ execAfter(1.0) { self.removeCardsAtPlaces(self.selectedIndexes) self.selectedIndexes = [] } } func removeCardsAtPlaces(places: Array<NSIndexPath>){ for index in selectedIndexes { let cardCell = collectionView.cellForItemAtIndexPath(index) as! CardCell cardCell.remove() } }
The remove()
function in CardCell
is similar to turnUp()
and turnDown()
, but instead of making a transition, it just performs an animation before hiding the cell:
func remove() { UIView.animateWithDuration(1, animations: { self.alpha = 0 }, completion: { completed in self.hidden = true }) }
We made the wrong move
Finally, if the cards are different, we need to turn them down:
func turnCardsFaceDown(){ execAfter(2.0) { self.downturnCardsAtPlaces(self.selectedIndexes) self.selectedIndexes = [] } } func downturnCardsAtPlaces(places: Array<NSIndexPath>){ for index in selectedIndexes { let cardCell = collectionView.cellForItemAtIndexPath(index) as! CardCell cardCell.downturn() } }
Et voilà! The game is completed
As you can see in the following screenshot, the game presents a smooth animation and nice images:
Note
The complete source can be downloaded from https://github.com/gscalzo/Swift2ByExample/tree/2_Memory_4_Complete.