Search code examples
swiftuiviewcontrolleruikitviewdidload

Conflicting calls to viewDidLoad() and another block of code


Screen in question I am trying to call the viewDidLoad() func here with the question() func a set number of times and only then call the code for the new view controller to be pushed. But, the code for both is getting executed at the same exact time such that as soon as the first round finishes, the view controller is pushed. I have tried using loops in different variations, but those attempts all led to similar results. Ideas? And thanks in advance!


var num1 = Int()
var num2 = Int()
var userInput = 0
    
@IBAction func numbers(_ sender: UIButton) {
        answerLabel.text = answerLabel.text! + String(sender.tag - 1)
        userInput = Int(answerLabel.text!)!
        
        answer()
    }

override func viewDidLoad() {
        super.viewDidLoad()
        
        // Views configuration
        question()
    }

func answer() {
    let answer = num1 + num2
        
    if userInput == answer {
        answerLabel.text = ""
        viewDidLoad()
        let scoreVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ScoreViewController") as! ScoreViewController
        self.navigationController?.pushViewController(scoreVC, animated: true)
    }
}


func question() {
        num1 = Int(arc4random_uniform(100))
        num2 = Int(arc4random_uniform(100))
        questionLabel.text = "\(num1)  +  \(num2)"
}

Solution

  • There are quit e few things that we can improve here. I'll start at basic. As these numbers are unset at the beginning we don't have to assign them yet. So instead of settings them to a default value we can leave them as Optionals which allows us to just define the type and leave them be for an assignment later on.

    var num1: Int?
    var num2: Int?
    

    Now we wanna show a Question. The thing is, the viewDidLoad shall only be used when - as the name suggests - the view loads. Because of this we simply create a Method and move our logic there. You already did that, I just renamed your function to a more speaking name

    viewDidLoad() {
        showNewQuestion()
    }
    
    showNewQuestion() { // previously question()
        num1 = Int(arc4random_uniform(100))
        num2 = Int(arc4random_uniform(100))
        questionLabel.text = "\(num1)  +  \(num2)"
    }
    

    So far so good. Now we gotta check the input against the random value in the Button send function. Instead of force unwrapping (!) it is better practise to safely unwrap (?).

    @IBAction func numbers(_ sender: UIButton) {
        guard let userInput = answerLabel.text, let userInputAsInt = Int(userInput) else { 
            return 
        }
        checkAnswer(userInput: userInputAsInt) // previously answere()
    }
    

    Lastly we improve your answer() function. The thing you left is to count how many questions have been answered. If we don't ever check that how should the program know when to show the score? To fix this problem we remember how many questions were asked (correctAnsweres ) and we define a threshold when to show the score (showScoreAfter)

    var correctAnsweres = 0
    var showScoreAfter = 5 
    
    func checkAnswer(userInputAsInt: Int) {
        let answer = num1 + num2
    
        if userInputAsInt != answers { // early exit if answered wrong
            return
        }
        correctAnsweres += 1
        answerLabel.text = ""
        
        //either show score
        if correctAnsweres % ShowScoreAfter == 0 {
            let scoreVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(identifier: "ScoreViewController") as! ScoreViewController
            self.navigationController?.pushViewController(scoreVC, animated: true)
        }
       // or new Question
        else {
            showNewQuestion()
        }
    }