I have a coding 'issue'.
I have a label, which text I want to change dynamically every 2 seconds.
I've done the following:
// WELCOME STRING ARRAY
let welcomeContainer:[String] = ["Welcome","Benvenuti","Bienvenue","Willkommen","üdvözlet","Dobrodošli","добро пожаловать","Witajcie","Bienvenido","Ласкаво просимо","Vitajte","欢迎你来"]
and then, rather than using a timerwithinterval
(which seemed to be too much for this simple task), I tried with the delay
method in my function inside for
loop:
func welcomeLabelChange() {
for i in 0..<welcomeContainer.count {
welcomeLabel.text = welcomeContainer[i]
delay(delay: 2.0, closure: {})
}
Unfortunately it's entirely skipping the delay... the for loop is executed instantly and just the last text in the array is displayed.
What am I doing wrong?
I found this OBJ-C answer, but it's suggesting an (old) NSTimer
implementation.
You can also use this function to delay something
//MARK: Delay func
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
and usage is :
delay(2) //Here you put time you want to delay
{
//your delayed code
}
Hope it will help you.
define those variables
var i = 0
let timer : Timer?
Place this timer in your view did load or wherever you want to start the label change
timer = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector:#selector(YourViewController.changeText), userInfo: nil, repeats: true)
and implement this method:
func changeText(){
if i>=welcomeContainer.count {
i = 0
}
welcomeLabel.text = welcomeContainer[i]
i += 1
}
when you want to stop it or change the view controller dont forget to call
timer.invalidate()
You can add sleep function
for i in 0..<welcomeContainer.count {
welcomeLabel.text = welcomeContainer[i]
sleep(2) // or sleep(UInt32(0.5)) if you need Double
}
With Timer
, you should be careful to call invalidate
of the Timer
in viewDidDisappear
or else you may not release the view controller.
Alternatively, you can use a GCD dispatch timer, in which you completely eliminate the strong reference cycle by using [weak self]
pattern:
@IBOutlet weak var welcomeLabel: UILabel!
var timer: DispatchSourceTimer!
override func viewDidLoad() {
super.viewDidLoad()
let welcomeStrings = ["Welcome", "Benvenuti", "Bienvenue", "Willkommen", "üdvözlet", "Dobrodošli", "добро пожаловать", "Witajcie", "Bienvenido", "Ласкаво просимо", "Vitajte", "欢迎你来"]
var index = welcomeStrings.startIndex
timer = DispatchSource.makeTimerSource(queue: .main)
timer.scheduleRepeating(deadline: .now(), interval: .seconds(2))
timer.setEventHandler { [weak self] in
self?.welcomeLabel.text = welcomeStrings[index]
index = index.advanced(by: 1)
if index == welcomeStrings.endIndex {
index = welcomeStrings.startIndex // if you really want to stop the timer and not have this repeat, call self?.timer.cancel()
}
}
timer.resume()
}
Marked answer doesn't delay loop iterations and you still get just the last value in the label.text.
You can solve it like this:
func showWelcome(_ iteration: Int = 0) {
let i = iteration>=self.welcomeContainer.count ? 0 : iteration
let message = self.welcomeContainer[i]
self.delay(2){
self.welcomeLabel.text = message
return self.showWelcome(i + 1)
}
}
Usage:
showWelcome()
If you want to keep it all inline you can do this:
var loop: ((Int) -> Void)!
loop = { [weak self] count in
guard count > 0 else { return }
//Do your stuff
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
loop(count - 1)
}
}
loop(10) //However many loops you want