Shady Actual February 2016

Swift code executing before display updates

So I've just started trying out coding in swift and I'm creating an extremely basic app (purely to experiment and learn) where you click a button and two playing cards appear on the screen. I'm trying to get it so that when the two playing cards are the same, the button to play again disables, the program pauses for a few seconds, then the button re-enables (so later I can add some 'win' text during the pause time).

Now the button and pausing fully works besides one problem. When testing, the program pauses and then when it finishes pausing the display then updates to show the two cards being equal. But while it pauses, it shows two random non equal cards.

I'm not sure why, seeing as the cards update before I check if they're equal, but I'm new to swift (literally last few days) so not sure how it works.

Any ideas? :)

@IBAction func playRoundTapped(sender: UIButton) {
    // Change the card image each time the play button is pressed using a random number generator
    self.firstCardImageView.image = UIImage(named: String(format: "card%i", arc4random_uniform(13)+1))
    self.secondCardImageView.image = UIImage(named: String(format: "card%i", arc4random_uniform(13)+1))

    // Check if the cards are equal
    if firstCardImageView.image == secondCardImageView.image && firstCardImageView.image != "card" {
        playRoundButton.userInteractionEnabled=false;
        NSThread.sleepForTimeInterval(4)
        playRoundButton.userInteractionEnabled=true;
    }
}

Answers


sergio February 2016

First off, a better solution is not pausing at all and using dispatch_after to change the playRoundButton button state after 4 seconds.

If you want to stick with pausing, then you should give time for the UI to update itself before pausing. E.g.,

dispatch_async(dispatch_get_main_thread(), {
  //Check if both cards are equal
  if firstCardImageView.image == secondCardImageView.image && firstCardImageView.image != "card" {
      playRoundButton.userInteractionEnabled=false;
      NSThread.sleepForTimeInterval(4)
      playRoundButton.userInteractionEnabled=true;
  }
});

The fact is, when you assign a new image to your buttons, the button is actually redrawn on screen only at the next run-loop cycle, so if you pause before that is run, no visual change can be seen...

Keep in mind that pausing on the main thread will make your app unresponsive during that timeframe.


Gary Makin February 2016

Don't sleep in the main thread as this will stop all interactions with your app. You need to replace:

NSThread.sleepForTimeInterval(4)
playRoundButton.userInteractionEnabled=true;

with:

let enableTime = dispatch_time(DISPATCH_TIME_NOW, Int64(4 * Double(NSEC_PER_SEC)))
dispatch_after(enableTime, dispatch_get_main_queue()) {
    playRoundButton.userInteractionEnabled=true;
}


Tim Vermeulen February 2016

You're comparing two UIImage instances, which doesn't work with == because it will only compare the pointers. In your case, it would be much easier to compare the numbers that generated those images.

Other than that, you're pausing the main thread, which takes care of updating the user interface, so it doesn't actually get a chance to do so. One way to solve this problem is by using NSTimer.

@IBAction func playRoundTapped(sender: UIButton) {
    let firstNumber = arc4random_uniform(13) + 1
    let secondNumber = arc4random_uniform(13) + 1

    firstCardImageView.image = UIImage(named: "card\(firstNumber)")
    secondCardImageView.image = UIImage(named: "card\(secondNumber)")

    if firstNumber == secondNumber {
        playRoundButton.userInteractionEnabled = false;
        NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: "enableButton", userInfo: nil, repeats: false)
    }
}

func enableButton() {
    playRoundButton.userInteractionEnabled = true;
}

Post Status

Asked in February 2016
Viewed 1,314 times
Voted 6
Answered 3 times

Search




Leave an answer