对于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)")
}
}
}
}
答案 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(...
。然后,您扩展了类以支持动画师的委托,并且在这两种方法上,只需更新您的文本字段或您使用的任何文本容器。另外,您也可以将这段代码放到您的类中,然后按照您到目前为止似乎一直在直接使用它。