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)")
}
}
}
}
最佳答案
似乎当用户第二次点击时基本上两个动画开始同时发生。由于您使用 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(...
)。并且您扩展您的类以支持动画师的委托(delegate),并且在这两种方法上只需更新您的文本字段或您正在使用的任何文本容器。或者您可以简单地将这段代码放入您的类中并直接使用它,就像您到目前为止一直在做的那样。
关于ios - 当点击手势执行时,带有 uitextfield 的逐字母动画同时执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52070108/