Display every iteration of a for in loop?

Hi everyone,

Does anyone have a solution to this?

The for in loop chooses 5 random numbers from a range of 1-4 and I’m trying to display each of those numbers from the for in loop on UILabel: mainLabel with a 2 second delay in between each. How can I achieve this?

I’ve tried a Timer object and can’t seem to get it right?

Thanks in advance for any help.

This is what I have at the moment (Note: I’ve taken out the faulty code)

struct Variables  {  
    static var targets = 4
    static var calls = 5
    static var delayTime = 3
}

// Call a random target by X amount of calls
         for _ in 1...Variables.calls {
                    
         // Randomize the range of the Targets
         let targetRange = Int.random(in: 1...Variables.targets)
                    
         // Set the mainLabel to display the called Target
         self.mainLabel.text = String(targetRange)
        }

Here is a solution for you:

import UIKit

struct Variables  {
    static var targets = 4
    static var calls = 5
    static var delayTime: Double = 2
}

class ViewController: UIViewController {

    @IBOutlet var mainLabel: UILabel!
    var arrayOfNumbers = [Int]()
    var arrayIndex = 0
    var timer = Timer()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        mainLabel.text = "Generating random numbers"
        
        generateRandomNumbers()
        startTimer()
    }
    
    func generateRandomNumbers() {
        
        //  Loop x number of times based on Variables.calls
        for _ in 1...Variables.calls {
            
            //  Randomize the range of the Targets
            let targetRange = Int.random(in: 1...Variables.targets)
            
            // Add the random to the array
            arrayOfNumbers.append(targetRange)
            print(targetRange)
        }
    }
    
    func startTimer() {
        
        //  Timer triggers at an interval defined by Variables.delayTime and calls displayNumber
        timer = Timer.scheduledTimer(timeInterval: Variables.delayTime, target: self, selector: #selector(displayNumber), userInfo: nil, repeats: true)
    }
    
    @objc func displayNumber() {
        mainLabel.text = "Random number \(arrayIndex + 1) of \(Variables.calls) is: \(arrayOfNumbers[arrayIndex])"
        
        //  Increment the array index ensuring that it does not go out of range
        if arrayIndex < arrayOfNumbers.count - 1 {
            arrayIndex += 1
        } else {
            
            //  If all the numbers have been displayed, stop the timer.
            timer.invalidate()
        }
    }
}
1 Like

You’re a champion! Thank you very much :slight_smile:

Okay, so I’ve implemented it into my project and it works perfectly. Displaying the numbers exactly how I wanted it to. The issue I have encountered now, is that I call all these functions on @IBAction func startRoundTapped which triggers a timer and then when that timer reaches zero, triggers the code you helped me with above. The problem though, is that when the timer reaches zero, it starts the delayTime for the display of random number. This adds on two seconds to the original timer.

Is there a way I can start the delay time AFTER the first number has been displayed?

I’m trying:

if self.time == 0 + Int(Variables.intervalTime)  {

// Code 

}

Which works perfectly on the first tap, but if I let the array run, and then tap the startButtonTapped again, It continues with the original problem. Note: I renamed delayTime -> intervalTime for clarity in my project

If I understand you correctly, you want to display the first number immediately after the random numbers have been generated and then introduce the delay showing the remaining numbers?

In that case add a call to displayNumber() in startTimer() just before the timer is set. ie:

func startTimer() {
    displayNumber()
    //  Timer triggers at an interval defined by Variables.delayTime and calls displayNumber
    timer = Timer.scheduledTimer(timeInterval: Variables.intervalTime, target: self, selector: #selector(displayNumber), userInfo: nil, repeats: true)
}
1 Like

No Chris that didn’t work. It crashed the app, presumably because displayNumber() has a call startTime.invalidate() or because the timers selector is display time?

Can I not issue an if statement that says if the timer is on its first trigger its invalidated and then if not it continues as normal?

Ah OK, in your application that may not work but in my code sample that does because my sample App has to be re-launched every time.

Maybe share your code so I can see what you are trying to do.

1 Like

This is the first view controller :slight_smile:

class FirstViewController: UIViewController {

// MARK: Variables
var delayTimer:Timer?
var time = Variables.delayTime
var arrayOfNumbers = [Int]()
var arrayIndex = 0
var intervalTimer = Timer()


// MARK: IB Outlets
@IBOutlet weak var startButton: UIButton!
@IBOutlet weak var mainLabel: UILabel!


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


// MARK: Generate random numbers & display with interval timer

// Generate Random Numbers
func generateRandomNumbers() {
    
    // Loop x number of times based on Variables.calls
    for _ in 1...Variables.calls {
        
        // Randomize the range of the Targets
        let targetRange = Int.random(in: 1...Variables.targets)
        
        // Add the random to the array
        arrayOfNumbers.append(targetRange)
        
        // Check the targetRange is correct (backend)
        print(targetRange)
        
    }
}


// Interval Timer
func startIntervalTimer() {
    
    // Timer triggers at an interval defined by Variables.intervalTime and calls displayNumber
    intervalTimer = Timer.scheduledTimer(timeInterval: Variables.intervalTime, target: self, selector: #selector(displayNumber), userInfo: nil, repeats: true)

}


// Display Each Random Number
@objc func displayNumber() {
    
    // Display the Array on the mainLabel
    mainLabel.text = "\(arrayOfNumbers[arrayIndex])"
    
    // Increment the array index ensuring that it does not go out of range
    if arrayIndex < arrayOfNumbers.count - 1 {
        arrayIndex += 1
        
    } else {
        // If all the numbers have been displayed, stop the timer.
        intervalTimer.invalidate()
        
    }
}



// MARK: Start the Round
@IBAction func startRoundTapped(_ sender: Any) {

    // Reset the time variable (called twice to stop double tap glitch)
    self.time = Variables.delayTime
    
    // Start Delay Timer
    delayTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { delaytTimerSecond in
        // Countdown interval
        self.time -= 1
        
        // If timer reaches zero
        if self.time == 0 {
            
            // Call the Interval Timer function
            self.startIntervalTimer()
            
            // Call the Random Number function
            self.generateRandomNumbers()
            
            // Reset the startDelay timer
            delaytTimerSecond.invalidate()
            
            // Reset the delayTime variable (called twice to stop double tap glitch)
            self.time = Variables.delayTime

        }
    }
}



} // END OF: viewDidLoad

Cheers Dylan,

I’ll have a crack at that tonight. I have to head off for the day. (just after 9am here at the moment in Perth).

1 Like

No worries Chris. Really appreciate it! :slight_smile: Have a good one

Hi Dylan,

I’ve had a bit of a play with the code you provided and come up with the following:

import UIKit

struct Variables  {
    static var targets = 20
    static var calls = 5
    static var intervalTime: Double = 2
}

class FirstViewController: UIViewController {
    
    // MARK: Variables
    var arrayOfNumbers = [Int]()
    var arrayIndex = 0
    var intervalTimer = Timer()
    
    
    // MARK: IB Outlets
    @IBOutlet weak var startButton: UIButton!   
    @IBOutlet var mainLabel: UILabel!
    
    
    // MARK: viewDidLoad
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        startButton.setTitle("Start", for: .normal)
    }
    
    
    // MARK: Generate random numbers & display with interval timer
    
    // Generate Random Numbers
    func generateRandomNumbers() {
        
        //  Reset the arrayIndex to 0 ready for the next set of random mnumbers to be generated
        arrayIndex = 0
        
        //  Remove all current random numbers from the array.
        arrayOfNumbers.removeAll()
        
        // Loop x number of times based on Variables.calls
        for _ in 1...Variables.calls {
            
            // Randomize the range of the Targets
            let targetRange = Int.random(in: 1...Variables.targets)
            
            // Add the random to the array
            arrayOfNumbers.append(targetRange)
            
            // Check the targetRange is correct (backend)
            print(targetRange)
            
        }
    }
    
    
    // Interval Timer
    func startIntervalTimer() {
        
        // Timer triggers at an interval defined by Variables.intervalTime and calls displayNumber
        intervalTimer = Timer.scheduledTimer(timeInterval: Variables.intervalTime, target: self, selector: #selector(displayNumber), userInfo: nil, repeats: true)
        
    }
    
    
    // Display Each Random Number
    @objc func displayNumber() {
        
        // Display the Array on the mainLabel
        mainLabel.text = "Random number \(arrayIndex + 1) = \(arrayOfNumbers[arrayIndex])"
        
        // Increment the array index ensuring that it does not go out of range
        if arrayIndex < arrayOfNumbers.count - 1 {
            arrayIndex += 1
                
        } else {
            // If all the numbers have been displayed, stop the timer.
            intervalTimer.invalidate()
            
            startButton.isEnabled = true
            startButton.setTitle("Start", for: .normal)
        }
    }
    
    
    
    // MARK: Start the Round
    @IBAction func startRoundTapped(_ sender: Any) {
        
        //  Deactivate the button to prevent double tap
        startButton.isEnabled = false
        startButton.setTitle("Start Disabled", for: .normal)
        
        print("Next set of ramdom numbers")
        
        // Call the Random Number function
        self.generateRandomNumbers()
        
        //  Display the first random number immediately and then let the time take care of the remainder
        displayNumber()
        
        // Call the Interval Timer function
        self.startIntervalTimer()
    }
}

Some points regarding the code:

  • I used the IBOutlet for the button (startButton) so that the Start button can be disabled after being tapped (to avoid inadvertent double taps) and then re-enabled after all the numbers have been displayed (code added to just after startRoundTapped() to disable and just after the intervalTimer.invalidate() to re-enable).
  • I also changed the text of the button so that it was obvious that it was disabled and then also changed it back after the button was re-enabled.
  • I swapped the two lines of code self.startIntervalTimer() and self.generateRandomNumbers() because you need to generate the random numbers before you attempt to display them.
  • The delay code was now irrelevant so removed it.
  • In gererateRandomNumbers() I added arrayIndex = 0 and arrayOfNumbers.removeAll() so that on each press of the Start button the arrayIndex is reset to 0 and the previous group of random numbers are removed from arrayOfNumbers.
  • In startRoundTapped() I added a call to displayNumber() so that the first number in the array is displayed and then the remainder of the numbers in the array are shown by the timer being triggered at the interval dictated by Variables.intervalTime

I think that just about covers it.
This is how the screen looks before Start tapped and during display of the numbers.

2 Likes

Chris you are an absolute mamba :smiley: I can’t thank you enough for the help!

2 Likes

No worries. Good luck with the rest of the project.

1 Like