简体   繁体   中英

Wait for Swift timers to finish

So I'm doing a simple game on swift 5 and I basically have a 3..2..1.. go timer to start the game and then a 3..2..1..stop timer to stop the game. And finally a function that displays the score. I need a way for each function call to wait for the timer to be done before the next one begins, any suggestions? Here's my code so far. (Also if you have any other suggestions on the app let me know as well, the end goal is to register how many taps of a button you can do in 3 seconds)

var seconds = 3 //Starting seconds
var countDownTimer = Timer()
var gameTimer = Timer()
var numberOfTaps = 0

override func viewDidLoad() {
    super.viewDidLoad()

    self.startCountdown(seconds: seconds)
    self.gameCountdown(seconds: seconds)
    self.displayFinalScore()


}

func startCountdown(seconds: Int) {
        countDownTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
            self?.seconds -= 1
            if self?.seconds == 0 {
                self?.countdownLabel.text = "Go!"
                timer.invalidate()
            } else if let seconds = self?.seconds {
                self?.countdownLabel.text = "\(seconds)"
            }
        }
}

func gameCountdown(seconds: Int) {
        gameTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
              self?.seconds -= 1
              if self?.seconds == 0 {
                  self?.countdownLabel.text = "Stop!"
                  timer.invalidate()
              } else if let seconds = self?.seconds {
                  self?.countdownLabel.text = "\(seconds)"
              }
          }
  }


    deinit {
        // ViewController going away.  Kill the timer.
        countDownTimer.invalidate()
    }




@IBOutlet weak var countdownLabel: UILabel!


@IBAction func tapMeButtonPressed(_ sender: UIButton) {
    if gameTimer.isValid {
        numberOfTaps += 1
    }
}

func displayFinalScore() {
    if !gameTimer.isValid && !countDownTimer.isValid {
        countdownLabel.text = "\(numberOfTaps)"
    }
}

The approach shouldn't be that the function calls wait for the timer to get finished, rather the timers should call the functions when they finish.

So, you need to move below function calls out of viewDidLoad and put them inside the Timer blocks.

self.gameCountdown(seconds: seconds)
self.displayFinalScore() 

Ie the function call self.gameCountdown(seconds: seconds) will go inside the timer block started in startCountdown . In that, when you are invalidating the timer when the seconds become 0, you call gameCountdown .

Similarly, in the timer started in gameCountdown , you call the self.displayFinalScore when the seconds become 0.

Few other suggestions. You should avoid checking properties in tapMeButtonPressed . You should rather disable and enable the tap me button instead. Ie enable it when you start the gameCountdown and disable it when it ends.

Similarly, you shouldn't need to check the state of the timers in displayFinalScore . It should just do one thing ie display the final score.

Will save you a lot of headaches later:). My 2 cents.

You should think about the states your game could be in. It could be -

  • setup - Establish the game
  • starting - The first three seconds
  • running - After the first three seconds but before the end
  • ending - In the final three seconds
  • ended - Time is up.

Each time your timer ticks you need to consider what action do you need to take and what state do you need to move to. You haven't said how long you want the game to last, but let's say it is 30 seconds.

When a new game is started, you are in the setup state; The button is disabled (ie. it doesn't react to taps) and you set the score to 0. You move to the starting state. In the starting you show the countdown. After three seconds you enable the button and move into the running state. Once you reach 27 seconds, you move into the ending state and show the end count down Finally time is up and you move into the ended state, disable the button and show the score.

You could code it something like this


enum GameState {

    case setup
    case starting
    case running
    case ending
    case ended
}

class ViewController: UIViewController {

    @IBOutlet weak var startButton: UIButton!
    @IBOutlet weak var tapButton: UIButton!
    @IBOutlet weak var countdownLabel: UILabel!

    var gameState = GameState.ended
    var gameTimer:Timer?
    var numberOfTaps = 0
    var gameStartTime = Date.distantPast
    let GAMEDURATION: TimeInterval = 30

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func startButtonTapped(_ sender: UIButton) {
        self.startGame()
    }

    @IBAction func tapMeButtonPressed(_ sender: UIButton) {
        self.numberOfTaps += 1
    }

    func startGame() {
        self.gameState = .setup

        self.gameTimer = Timer.scheduledTimer(withTimeInterval:0.1, repeats: true) { timer in

            let elapsedTime = -self.gameStartTime.timeIntervalSinceNow
            let timeRemaining = self.GAMEDURATION-elapsedTime
            switch self.gameState {
            case .setup:
                self.gameStartTime = Date()
                self.tapButton.isEnabled = false
                self.startButton.isEnabled = false
                self.numberOfTaps = 0
                self.gameState = .starting
            case .starting:
                if elapsedTime > 2.5 {
                    self.gameState = .running
                    self.tapButton.isEnabled = true
                    self.countdownLabel.text  = "Go!"
                } else {
                    let countdown = Int(3-round(elapsedTime))
                    self.countdownLabel.text = "\(countdown)"
                }
            case .running:
                if timeRemaining < 4 {
                    self.gameState = .ending
                }
            case .ending:
                let countdown = Int(timeRemaining)
                self.countdownLabel.text = "\(countdown)"
                if timeRemaining < 1 {
                    self.countdownLabel.text = "Stop"
                    self.gameState = .ended
                    self.tapButton.isEnabled = false
                }
            case .ended:
                if timeRemaining <= 0 {
                    self.countdownLabel.text = "You tapped the button \(self.numberOfTaps) times"
                    self.startButton.isEnabled = true
                    self.gameTimer?.invalidate()
                    self.gameTimer = nil
                }
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM