11

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.

Community
  • 1
  • 1
Alex
  • 271
  • 2
  • 4
  • 18

6 Answers6

21

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.

0ndre_
  • 3,577
  • 6
  • 26
  • 44
7

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()
ben
  • 900
  • 9
  • 17
  • Thanks @ben your suggestion is exactly what I wanted to do. On the other hand I wanted to explore the "delay" function rather than a timer! But your's works perfectly. – Alex Sep 30 '16 at 12:59
5

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
}
Booharin
  • 753
  • 10
  • 10
3

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
Cameron Porter
  • 801
  • 6
  • 15
2

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()
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

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()