打字机效果文本动画

8
我正在尝试使用UILabel创建打字机动画效果,但是找不到任何答案。 UILabel是否是正确的对象? 我想要将一系列字符串打印到屏幕上,例如“登录中...打开文件夹...正在重新启动系统...”等。 我应该提醒一下,我对编码很新,并且尝试搜索文档和API参考,但没有成功。 如果值得一提的话,我目前正在学习SWIFT。

可能是UILabel的逐字动画?的重复问题。 - shim
4个回答

15

根据这个答案: UILabel的逐字动画?

我已经将其更新到Swift 4,并使用DispatchWorkItem解决了CPU动画问题,以创建一个队列。

Swift 4

extension UILabel {
    func setTextWithTypeAnimation(typedText: String, characterDelay: TimeInterval = 5.0) {
        text = ""
        var writingTask: DispatchWorkItem?
        writingTask = DispatchWorkItem { [weak weakSelf = self] in
            for character in typedText {
                DispatchQueue.main.async {
                    weakSelf?.text!.append(character)
                }
                Thread.sleep(forTimeInterval: characterDelay/100)
            }
        }
        
        if let task = writingTask {
            let queue = DispatchQueue(label: "typespeed", qos: DispatchQoS.userInteractive)
            queue.asyncAfter(deadline: .now() + 0.05, execute: task)
        }
    }
    
}

使用方法

label.setTextWithTypeAnimation(typedText: text, characterDelay:  10) //less delay is faster
Swift 5
func setTyping(text: String, characterDelay: TimeInterval = 5.0) {
  self.text = ""
    
  let writingTask = DispatchWorkItem { [weak self] in
    text.forEach { char in
      DispatchQueue.main.async {
        self?.text?.append(char)
      }
      Thread.sleep(forTimeInterval: characterDelay/100)
    }
  }
    
  let queue: DispatchQueue = .init(label: "typespeed", qos: .userInteractive)
  queue.asyncAfter(deadline: .now() + 0.05, execute: writingTask)
}

用法

label.setTyping(text: "Your text")

1
它似乎能解决问题,但问题在于如果在动画结束之前设置一个新字符串,它将把两个字符串粘在一起。如果有一种方法可以在开始新任务之前取消任务,那就完美了。 - Ryuuzaki Julio
@RyuuzakiJulio 我认为你可以禁用字符串输入源,并在写入任务中创建一个计数器来检查它是否已结束。换句话说,当计数器等于字符数时,输入将再次启用。 - Dimas Mendes
@dimas-mendes 这是否意味着在第一次动画完成之前,所有新的输入都将被禁用? 因此,如果我有一个更重要的新文本进来,它将被忽略,直到先前的消息完成? 是否有一种方法可以保留任务并在出现新任务时取消它? - Ryuuzaki Julio
@RyuuzakiJulio 是的,我不知道你的应用程序内部的逻辑,但我认为你可以创建一个临时结构来存储所有的写作任务,如果你需要取消,你可以在每个待处理任务上调用“workItem?.cancel()”。 - Dimas Mendes
@dimas-mendes 我的应用程序会对蓝牙/用户按钮按下事件做出反应。我有一个“状态”标签,显示最新的事件,我想加入一个动画效果会更好。逻辑上讲,将消息列表按顺序显示是很合理的。但是,我对你的解决方案有疑问,因为任务是在UILabel扩展函数内创建的,所以一旦再次调用,就没有记录先前的任务,如何将它们存储在某个地方以便在新任务到来之前取消它们?我无法在UILabel扩展中创建任务数组。 - Ryuuzaki Julio
@RyuuzakiJulio UIExtensions无法存储属性,但是您可以将结构作为参数传递到setTextWithTypeAnimation中,对吗?问题在于您需要通过引用传递数组,使用"inout" "allTasks: inout [DispatchWorkItem]",然后在let块内使用append将值添加到数组中"allTasks.append(writingTask!)"。 - Dimas Mendes

9
更新:Xcode 7.0 GM • Swift 2.0
import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var myTypeWriter: UITextField!
    let myText = Array("Hello World !!!".characters)
    var myCounter = 0
    var timer:NSTimer?
    func fireTimer(){
        timer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: "typeLetter", userInfo: nil, repeats: true)
    }
    func typeLetter(){
        if myCounter < myText.count {
            myTypeWriter.text = myTypeWriter.text! + String(myText[myCounter])
            let randomInterval = Double((arc4random_uniform(8)+1))/20
            timer?.invalidate()
            timer = NSTimer.scheduledTimerWithTimeInterval(randomInterval, target: self, selector: "typeLetter", userInfo: nil, repeats: false)
        } else {
            timer?.invalidate()
        }
        myCounter++
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        fireTimer()
        // Do any additional setup after loading the view, typically from a nib.
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

很好,迫不及待想试一下这个。 - K2Digital
@Leo Dabus,致命错误:在解包可选值时意外发现了nil (lldb)myTypeWriter.text = myTypeWriter.text! + String(myText[myCounter]) - nycdanie

9

我写了一个UILabel的子类,名为CLTypingLabel,可在GitHub上获取。这个子类可以满足你的需求。

安装CocoaPods后,将以下内容添加到您的Podfile中以使用它:

pod 'CLTypingLabel'

示例代码

将标签的类从UILabel更改为CLTypingLabel; enter image description here

@IBOutlet weak var myTypeWriterLabel: CLTypingLabel!

在运行时,设置标签的文本将自动触发动画:

myTypeWriterLabel.text = "This is a demo of typing label animation..."

您可以自定义每个字符之间的时间间隔:
myTypeWriterLabel.charInterval = 0.08 //optional, default is 0.1

您可以随时暂停打字动画:
myTypeWriterLabel.pauseTyping() //this will pause the typing animation
myTypeWriterLabel.continueTyping() //this will continue paused typing animation

此外,Cocoapods配套了一个示例项目CLTypingLabel


这是一个不错的库,但现在在某些设备上不支持最新的iOS 16。 - Neeraj Joshi

0

使用计时器实现打字机效果动画的我的版本:

    var text = "text"
    
    _ = Timer.scheduledTimer(
        withTimeInterval: 0.1,
        repeats: true
    ) { [weak self] timer in
        let char = text.removeFirst()
        
        self?.yourLabel.text?.append(char.description)
        
        if text.isEmpty {
            timer.invalidate()
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接