当执行点击手势时,带有uitextfield的逐字母动画正在同时执行

时间:2018-08-29 05:35:57

标签: ios swift animation uitextview

对于Stack Overflow来说是新手,而对于Swift来说又是新手。我试图在敲击屏幕时执行逐个字母的动画,但是当多次敲击屏幕时,字母显得乱七八糟。

我很确定这可能是因为下一行文本正在同时设置动画,而当前行正在设置动画,但是我不确定如何确定当前行是在下一个动画之前完成动画。正确的方法是什么?

   @objc func handleTap(sender: UITapGestureRecognizer? = nil) {



    if counter < textArray.count {
        storyTextView.text = textArray[counter] //
        storyTextView.animate(newText: storyTextView.text ?? textArray[counter], characterDelay: 0.1)
    counter += 1


    }
}

extension UITextView {

    func animate(newText: String, characterDelay: TimeInterval) {

    DispatchQueue.main.sync {

        self.text = ""

        for (index, character) in newText.characters.enumerated() {

            DispatchQueue.main.asyncAfter(deadline: .now() + characterDelay * Double(index)) {
                self.text?.append(character) // animation function is running at same time

                print("characterDelay \(characterDelay) index \(index)")
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

似乎用户第二次点击时,基本上同时开始出现2个动画。由于您使用2种不同的文本作为源,并逐个字符地附加以创建新的字符串,因此您可能会从中获得交错的字符串。

您没有指定用户第二次点击时实际期望的结果。最简单的方法是简单地结束当前动画并填充整个当前字符串。最困难的是要同时为两个动画,而我现在甚至不想深入思考。

我相信在您的情况下,使用计时器比使用调度要好。您可以创建如下内容:

let timer = Timer.scheduledTimer(withTimeInterval: characterDelay, repeats: true, block: {{ _ in
     // Append the character here
}})

最好执行以下操作来耗尽字符串而不是建立索引:

let newText = oldText + String(textToAppend.remove(at: textToAppend.startIndex))

因此,这将从源字符串中删除第一个字符,同时将其附加到目标字符串中。

现在将它们放在一起,我将执行以下操作:

protocol StringAnimatorDelegate: class {
    func stringAnimator(_ sender: StringAnimator, didUpdateStringTo string: String)
    func stringAnimator(_ sender: StringAnimator, didFinishWithString string: String)
}

class StringAnimator {

    var delegate: StringAnimatorDelegate?

    private(set) var text: String = ""
    private var timerWithString: (timer: Timer, stringToAppend: String)?

    func animateText(_ stringToAppend: String, intervalDuration: TimeInterval) {
        if let timerWithString = timerWithString {
            self.timerWithString = nil
            // We have a string already to append. Finish it
            timerWithString.timer.invalidate() // Stop the previous timer
            self.text = self.text + timerWithString.stringToAppend // Append whatever is left to append
            self.delegate?.stringAnimator(self, didFinishWithString: self.text)
        }

        // Create a new timer
        let timer = Timer.scheduledTimer(withTimeInterval: intervalDuration, repeats: true) { timer in
            guard let string = self.timerWithString?.stringToAppend, string.count > 0 else {
                // String is either nil or depleted. Finish timer.
                self.timerWithString?.timer.invalidate()
                self.timerWithString = nil
                self.delegate?.stringAnimator(self, didFinishWithString: self.text)
                return
            }
            var depletingString = string // Make a mutable copy of string to deplete so we may modify it
            self.text = self.text + String(depletingString.remove(at: depletingString.startIndex)) // Modify both strings
            self.timerWithString = (timer, depletingString) // Assign new values
            self.delegate?.stringAnimator(self, didUpdateStringTo: self.text)
        }
        self.timerWithString = (timer, stringToAppend)
    }

}

现在这是整个动画字符串的系统。您可以创建一个实例,例如:

let animator = StringAnimator()
animator.delegate = self

然后例如在点击时,像现在一样简单地调用animator.animateText(...。然后,您扩展了类以支持动画师的委托,并且在这两种方法上,只需更新您的文本字段或您使用的任何文本容器。另外,您也可以将这段代码放到您的类中,然后按照您到目前为止似乎一直在直接使用它。