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)
}
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()
}
}
}
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?
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)
}
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?
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
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.