NEW! Master Swift design patterns with my latest book! >>

< Previous: Creating a particle system using CAEmitterLayer   Next: How to measure touch strength using 3D Touch >

Wiggling cards and background music with AVAudioPlayer

The last part of our misdirection is going to be truly evil. That being said, it is entirely optional because I won't be teaching any vital new techniques here – I just enjoy screwing with my friends' heads!

We're going to add two more simple distractions to our app. First, we're going to make random cards move ever so slightly on the screen. The movement has to be small so that people catch it in the corner of their eye, but then aren't 100% sure anything actually happened. Second, we're going to add some background music to make people wonder whether there's something in the sound effects that tells you where the star is.

Making the cards move just a bit is easy thanks to the method perform(_:with:afterDelay:) that I introduced earlier. We're going to write a new method that scales a card so that it's a mere 1% larger than normal, before dropping it down again. To make things more interesting, we want this animation to happen only occasionally, so the users aren't sure when it will happen again.

This misdirection is clever because human eyes are extremely sensitive to motion at the edges of vision, so your eye notices a card moves and jumps to it, but of course by then our animation has stopped so your user isn't sure whether anything happened. If you want to create a more pronounced effect, just increase the transform scale that gets applied.

Open CardViewController.swift for editing. We need to use some randomization, so please add an import GameplayKit at the top, then add this new method somewhere in the class:

@objc func wiggle() {
    if GKRandomSource.sharedRandom().nextInt(upperBound: 4) == 1 {
        UIView.animate(withDuration: 0.2, delay: 0, options: .allowUserInteraction, animations: {
            self.back.transform = CGAffineTransform(scaleX: 1.01, y: 1.01)
        }) { _ in
            self.back.transform = CGAffineTransform.identity
        }

        perform(#selector(wiggle), with: nil, afterDelay: 8)
    } else {
        perform(#selector(wiggle), with: nil, afterDelay: 2)
    }
}

There are two things of interest in that new method. First, I've used the .allowUserInteraction animation option so that users can tap a card even when it's animating. Second, the method calls itself so that the wiggle animation happens repeatedly, but, in a particularly evil twist, the delay is much longer after a card already moved. This means if someone's eye jumps to a card when they think it moved, they'll have to stare at it for a full eight seconds before it moves again.

Once the wiggle() method has been called once it will carry on calling itself, so we just need to make that initial call to get things moving. To do that, add this code to the end of viewDidLoad() for the card view controller:

perform(#selector(wiggle), with: nil, afterDelay: 1)

The very last piece of misdirection is an easy one: making some music play. Some mystic-sounding music was in the Content folder you should have downloaded from GitHub in the first chapter, and is a piece of music called "Phantom from Space" by Kevin MacLeod. It's licensed under Creative Commons Attribution 3.0 – see this link for more information.

You should already have added the Content folder to your project, so all that's left is to use it. This is done with four small changes in ViewController.swift, starting with this import to the top:

import AVFoundation

Now create a property to hold our music audio:

var music: AVAudioPlayer!

Next we need to create a playMusic() method that loads in the music and plays it. This is almost identical to code we've covered before, but there is a small change because we need the music to loop. This is done by setting the audio player's numberOfLoops property to any negative number, such as -1. Here's the new method, again for ViewController.swift:

func playMusic() {
    if let musicURL = Bundle.main.url(forResource: "PhantomFromSpace", withExtension: "mp3") {
        if let audioPlayer = try? AVAudioPlayer(contentsOf: musicURL) {
            music = audioPlayer
            music.numberOfLoops = -1
            music.play()
        }
    }
}

The fourth and final change is just to call that new playMusic() method from within the view controller's viewDidLoad() method. So, add this to the end:

playMusic()

That completes our misdirections: we've added a shifting color gradient, we've added falling stars, we've made the cards move, and now we've added music too. With so many distractions in place hopefully your friends won't be able to guess the trick.

Speaking of tricks, that's our very next job: how to fix the app so you always guess correctly!

Want to build macOS apps?

Hacking with macOS delivers 18 awesome projects that teach you macOS development in no time – take control of your desktop today!

< Previous: Creating a particle system using CAEmitterLayer   Next: How to measure touch strength using 3D Touch >
MASTER SWIFT NOW
Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Practical iOS 11 Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let me know!

Click here to visit the Hacking with Swift store >>