You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UITextView长文本打字机效果卡顿延迟问题求助

解决UITextView长文本打字机效果卡顿、不同步问题

问题根源

常见的逐字符延迟方案(比如用DispatchQueue.asyncAfter逐个添加字符)存在两个核心问题:

  1. 调度延迟累积:每个字符的延迟基于初始时间计算,但系统调度任务时的微小延迟会在长文本下不断累积,导致动画越来越慢、不同步。
  2. 频繁重绘开销:每次仅更新一个字符,会触发UITextView频繁重绘,长文本下主线程负载过高,引发卡顿。

优化方案:用CADisplayLink实现同步流畅的打字机效果

CADisplayLink与屏幕刷新率同步,每帧更新一次内容,既避免了调度延迟累积,又能通过控制每帧更新的字符数减少重绘次数。

完整TypewriterTextView实现

import UIKit

class TypewriterTextView: UITextView {
    private var displayLink: CADisplayLink?
    private var fullText: String = ""
    private var currentIndex: Int = 0
    // 每帧显示的字符数,可根据需求调整
    private var charsPerFrame: Int = 1
    // 单个字符的间隔时间(秒),与charsPerFrame配合控制整体速度
    private var typingSpeed: TimeInterval = 0.05

    func startTyping(with text: String) {
        fullText = text
        currentIndex = 0
        text = ""
        
        // 终止之前的动画
        displayLink?.invalidate()
        
        // 根据屏幕刷新率计算每帧应显示的字符数
        let framesPerSecond = 60.0
        charsPerFrame = Int(framesPerSecond * typingSpeed)
        charsPerFrame = max(charsPerFrame, 1)
        
        displayLink = CADisplayLink(target: self, selector: #selector(updateText))
        displayLink?.add(to: .main, forMode: .common)
    }

    func stopTyping() {
        displayLink?.invalidate()
        displayLink = nil
        text = fullText
    }

    @objc private func updateText() {
        guard currentIndex < fullText.count else {
            stopTyping()
            return
        }
        
        // 计算当前帧要显示的文本范围
        let endIndex = min(currentIndex + charsPerFrame, fullText.count)
        let substring = fullText[fullText.startIndex..<fullText.index(fullText.startIndex, offsetBy: endIndex)]
        text = String(substring)
        
        // 自动滚动到文本末尾,保持内容可视
        let range = NSRange(location: text.count, length: 0)
        scrollRangeToVisible(range)
        
        currentIndex = endIndex
    }

    deinit {
        displayLink?.invalidate()
    }
}

测试调用代码

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let typewriterView = TypewriterTextView(frame: CGRect(x: 20, y: 100, width: view.bounds.width - 40, height: 300))
        typewriterView.font = .systemFont(ofSize: 16)
        typewriterView.backgroundColor = .lightGray
        view.addSubview(typewriterView)
        
        // 替换为你的长文本内容
        let longTestText = "这里可以放入任意长度的测试文本,无论是几百字还是几千字,这个方案都能保持流畅的打字机效果,不会出现卡顿或不同步的问题。你可以调整typingSpeed和charsPerFrame参数来控制打字速度,满足不同场景的需求。"
        typewriterView.startTyping(with: longTestText)
    }
}

方案优势

  1. 同步流畅:CADisplayLink绑定屏幕刷新率,动画与UI刷新完全同步,不会出现累积延迟。
  2. 低开销:每帧可更新多个字符,大幅减少UITextView的重绘次数,长文本下主线程压力更小。
  3. 可定制性:通过typingSpeed和charsPerFrame可以灵活调整打字速度,适配不同需求。

内容的提问来源于stack exchange,提问作者impo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 11:43:11