0

i'm trying to make animation typing words in label. With short text everything works good, but if i put a bit longer it starts writing by parts of the word, not by one letter. whats wrong in my code? And how to fix it?

extension UILabel {
    func animate(newText: String, characterDelay: TimeInterval) {
        DispatchQueue.main.async {
            self.text = ""
            for (index, character) in newText.enumerated() {
                DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay * Double(index)) {
                    self.text?.append(character)
                    self.fadeTransition(0.2)
                }
            }
        }
    }
}

extension UIView {
    
    func fadeTransition(_ duration:CFTimeInterval) {
        let animation = CATransition()
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        animation.type = CATransitionType.fade
        animation.duration = duration
        layer.add(animation, forKey: CATransitionType.fade.rawValue)
    }
}

next in viewDidLoad I called func:

override func viewDidLoad() {
        super.viewDidLoad()
        Label.animate(newText: """
Здесь много текста.
""", characterDelay: 0.1)
}
n0n4m3rZ
  • 11
  • 1
  • I've experienced the same issue, the solution is called DispatchWorkItem – oneshot Jan 21 '21 at 23:31
  • 2
    The problem is timer coalescing, where as part of a power saving feature, events that are scheduled within 10% of each other are coalesced to run together. But rather than working around that (with “strict” scheduling features), I would suggest not using this pattern at all. Use a repeating `Timer` instead, having each call add another letter. If you, for example, have to dismiss your view, the timer is easily invalidated, but if you schedule a bunch of GCD items to run, you then have a mess to keep track of all of them, canceling them, etc. – Rob Jan 21 '21 at 23:37
  • See https://stackoverflow.com/a/61257639/1271826 or https://stackoverflow.com/q/64662453/1271826 or https://stackoverflow.com/a/57213698/1271826 or ... – Rob Jan 21 '21 at 23:44
  • thank you guys! yours answers help me to read more about GCD, now i understand more about сoncurrency! – n0n4m3rZ Jan 22 '21 at 22:51

2 Answers2

0

find another answer:

extension UILabel {
    func animation(typing value: String, duration: Double){
        for char in value {
            self.text?.append(char)
            RunLoop.current.run(until: Date() + duration)
        }
    }
}

and called in viewDidAppear, not in viewDidLoad

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    Label.text = ""
    Label.animation(typing: "Здесь много всякого разного текста.", duration: 0.1)
}

i don't know is it right or not, but it works )

n0n4m3rZ
  • 11
  • 1
0

This one will work as expected

 override func viewDidLoad() {
    super.viewDidLoad()
    Label.text = ""
    var characterIndex = 0.0
    let titleText = "Sometext"
    for letter in titletexxt {
        Timer.scheduledTimer(withTimeInterval: 0.1 * characterIndex, repeats: false) { timer in
            self.Label.text?.append(letter)
        }
        characterIndex += 1
    }
}
Montes
  • 1
  • 4