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

如何优化UITextView以消除首次滚动卡顿问题?

解决UITextView首次滚动卡顿的方案

针对你遇到的首次滚动卡顿问题,核心原因是主线程同步创建大量富文本+滚动时才触发文本布局计算,可以通过以下几个方向优化:

1. 后台线程创建富文本,主线程赋值

把富文本的构建逻辑放到后台队列,避免阻塞主线程:

override func viewDidLoad() {
    super.viewDidLoad()
    
    Track(id: track!.id).getLyrics { [weak self] response in
        guard let self = self, let text = response?.lyrics_text else { return }
        
        var writtenBy = response?.lyrics_writers ?? ""
        if writtenBy.isEmpty {
            writtenBy = self.track?.album?.artistsString() ?? ""
        }
        
        // 后台线程处理富文本
        DispatchQueue.global(qos: .userInitiated).async {
            let style = NSMutableParagraphStyle()
            style.lineSpacing = 10
            let attributes = [
                NSAttributedString.Key.font: UIFont(name: "Helvetica", size: 19)!,
                NSAttributedString.Key.foregroundColor: UIColor(named: "Primary Material")!,
                NSAttributedString.Key.paragraphStyle: style
            ]
            
            let fullText = self.writtenByPhrase + " " + writtenBy + "\n\n" + text
            let attributedText = NSMutableAttributedString(string: fullText, attributes: attributes)
            
            attributedText.addAttribute(.font, 
                                       value: UIFont(name: "Helvetica-Bold", size: 19)!, 
                                       range: NSRange(location: 0, length: self.writtenByPhrase.count))
            
            // 主线程更新UI
            DispatchQueue.main.async {
                self.textView?.attributedText = attributedText
                self.textView?.scrollRangeToVisible(NSRange(location: 0, length: 0))
                
                // 提前触发布局计算
                self.triggerTextViewLayout()
            }
        }
    }
    
    // 提前配置文本容器,减少回调内操作
    textView?.textContainer.lineFragmentPadding = 20
    textView?.textContainerInset = UIEdgeInsets(top: 20, left: 0, bottom: 200, right: 0)
}

// 主动触发文本布局,避免滚动时才计算
private func triggerTextViewLayout() {
    guard let textView = textView else { return }
    textView.layoutManager.ensureLayout(for: textView.textContainer)
    textView.setNeedsLayout()
    textView.layoutIfNeeded()
}

2. 提前缓存字体

UIFont(name:...)首次调用会加载字体文件,可能产生IO延迟。建议在App启动阶段提前缓存所需字体:

// 在AppDelegate或全局初始化类中缓存
let cachedHelvetica = UIFont(name: "Helvetica", size: 19)!
let cachedHelveticaBold = UIFont(name: "Helvetica-Bold", size: 19)!

// 后续直接使用缓存的字体
let attributes = [
    NSAttributedString.Key.font: cachedHelvetica,
    // ...其他属性
]

3. 开启异步绘制优化

对于超长文本,可开启异步绘制减少主线程压力:

// 在viewDidLoad中设置
textView?.layer.drawsAsynchronously = true
textView?.layoutManager.allowsNonContiguousLayout = true // 默认已开启,明确设置更稳妥

关键优化逻辑

  • 富文本创建属于CPU密集型操作,后台处理避免阻塞主线程;
  • 主动触发ensureLayout让布局管理器提前计算文本排版,滚动时直接复用结果;
  • 字体缓存消除首次加载字体的IO开销;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 17:57:40