Update title of button in for loop in swift UIButton

I have been trying to change the title of my UIButton every iteration of a for loop in swift, but it doesn't work as the titles are not being updated. Heres the code:


@IBOutlet var generateButton: UIButton!
var x: Double = 0.0
for i in 0...nTokens {
  x = (Double(i)/Double(nTokens)) * 100
  print(self.generateButton.isHighlighted)
  self.generateButton.setTitle("\(x) %", for: .highlighted)
  self.generateButton.titleLabel!.font = UIFont.systemFont(ofSize: 20)
  sleep(3)
}

Have already linked generateButton to the correct button in the view, and self.generateButton.isHighlighted always print true. What am I doing wrong? Thanks

Accepted Reply

Several problems here:


- you set for .hilited and so that does not change when you display at the end

- everything is in the main thread


I tried this, that works


     @IBAction func generate(_ sender: UIButton) {
          var x: Double = 0.0
          let nTokens = 3
          DispatchQueue.global().async {
               for i in 0...nTokens {
                    x = (Double(i)/Double(nTokens)) * 100
                    DispatchQueue.main.async { () -> Void in
                         print(self.generateButton.isHighlighted, "\(x) %")
                         self.generateButton.setTitle("\(x) %", for: .normal) //.highlighted)
                         self.generateButton.titleLabel!.font = UIFont.systemFont(ofSize: 20)
                    }
                    sleep(1)
               }
          }
     }


Note that sleep is not in the main thread.

Replies

You should better show whole method including your code, showing all relevant parts of the class would be better.


I assume your code exists (except the IBOutlet declaration) exists in an action method.


One thing you should know is that all actual view updates happen at a certain time after the action method is terminated.

So, any updates done while executing the action method would have no effect.


A simple advise: never call `sleep` or any other waiting functions.

That might make your app irresponsible and can be a cause that your app would not be approved.


If you want process something with delay, you can use `Timer`.


An example:

    var buttonUpdatingTimer: Timer?
    var updatingCount: Int = 0
    
    @IBAction func buttonPressed(_ button: UIButton) {
        if let oldTimer = buttonUpdatingTimer {
            oldTimer.invalidate()
        }
        self.updatingCount = 0
        buttonUpdatingTimer = Timer(fire: Date(), interval: 3, repeats: true) {timer in
            let count = self.updatingCount
            if count > self.nTokens {
                self.buttonUpdatingTimer?.invalidate()
                self.buttonUpdatingTimer = nil
                self.generateButton.isHighlighted = false
                return
            }
            self.updatingCount = count + 1
            
            let x = (Double(count)/Double(self.nTokens)) * 100
            print(button.isHighlighted)
            self.generateButton.isHighlighted = true
            self.generateButton.setTitle("\(x) %", for: .highlighted)
            self.generateButton.titleLabel!.font = UIFont.systemFont(ofSize: 20)
        }
        RunLoop.main.add(buttonUpdatingTimer!, forMode: .default)
    }

Several problems here:


- you set for .hilited and so that does not change when you display at the end

- everything is in the main thread


I tried this, that works


     @IBAction func generate(_ sender: UIButton) {
          var x: Double = 0.0
          let nTokens = 3
          DispatchQueue.global().async {
               for i in 0...nTokens {
                    x = (Double(i)/Double(nTokens)) * 100
                    DispatchQueue.main.async { () -> Void in
                         print(self.generateButton.isHighlighted, "\(x) %")
                         self.generateButton.setTitle("\(x) %", for: .normal) //.highlighted)
                         self.generateButton.titleLabel!.font = UIFont.systemFont(ofSize: 20)
                    }
                    sleep(1)
               }
          }
     }


Note that sleep is not in the main thread.

Thanks for the advice. I was using sleep to test the loop in place of a time-consuming function. I didn't know actual view updates happen at a certain time after the action method is terminated, so what I'm missing is to run the update in a separate thread.

Thanks alot this works for me. So I'll have to update the button asynchronously on the main thread.