TJMac
TJMac

Reputation: 159

asyncAfter not delaying the first execution by specified time

I'm trying to animate a textview to get the string characters to appear one by one, then also disappear one by one starting with the first character after a 0.5 second delay.

I am close, the only issue I have is that the very first character gets removed immediately so it's as if it never appeared. Any ideas, here's my function:

extension UITextView {

    func animate(newText: String) {

        DispatchQueue.main.async {

            self.text = ""

            for (index, character) in newText.enumerated() {
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1 * Double(index)) {
                    self.text?.append(character)
                }

                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index)) {
                    self.text?.remove(at: newText.startIndex)
                }
            }
        }
    }
}

Upvotes: 1

Views: 209

Answers (1)

Sweeper
Sweeper

Reputation: 271595

The problem is that the first character has an index of 0, so the delay is .now() + 0.5 * 0, which simplifies to just .now().

Add a constant to the delay:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index) + 0.5) {
                                                                    ^^^^^^

This will cause the first character to disappear 1 second later.

Alternatively:

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(index + 1)) {

In addition, using a Timer here can be more suitable if your text is long, as Rob has said n the comments.

var index = 0
let characterArray = Array(newText)

Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { (timer) in
    textView.text! += "\(characterArray[index])"
    index += 1
    if index == characterArray.endIndex {
        timer.invalidate()
    }
}

Upvotes: 1

Related Questions