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

UITextView插入NSTextAttachment图片CPU过高、UI卡顿问题求助

问题根源

现有代码的核心性能损耗来自以下几个问题:

  • 重复执行文本插入逻辑:你已经手动调用txtViewShow.textStorage.replaceCharacters修改了文本内容,最后仍然返回true,会触发系统默认的文本插入行为,等于同一段范围执行了两次修改操作,冗余计算直接拉高CPU占用
  • 图片无缓存重复解码:每次匹配到对应文本都要从Data实时解码生成UIImage,图片解码是CPU密集型操作,多次触发会占用大量CPU资源
  • 未限制文本附件尺寸:没有给NSTextAttachment设置固定bounds,UITextView渲染时会每次实时计算图片的显示尺寸,插入图片越多重复计算量越大
  • 遍历匹配效率低:每次输入都遍历整个arrDataModel数组做匹配,数组元素多的时候会产生不必要的遍历开销

优化方案

1. 修正回调返回值

手动修改textStorage后直接返回false,阻止系统执行默认插入逻辑,避免重复修改文本

2. 提前缓存解码后的UIImage

在arrDataModel初始化时就把imageData转成UIImage并缓存,避免每次插入时重复解码

3. 固定NSTextAttachment显示尺寸

插入附件前提前设置好bounds属性,避免渲染时实时计算

4. 优化匹配逻辑

把arrDataModel的匹配规则改成字典映射,用textDefaul作为key,对应的数据模型作为value,O(1)复杂度匹配无需遍历数组

优化后示例代码

// 提前在外部做好字典映射和图片缓存,不用每次输入都计算
lazy var textMapping: [String: UIImage] = {
    var map = [String: UIImage]()
    for data in arrDataModel {
        guard let image = UIImage(data: data.imageData) else { continue }
        // 提前把图片缩放到需要的尺寸,缓存处理后的结果
        let targetSize = CGSize(width: 20, height: 20) // 自行调整目标显示尺寸
        UIGraphicsBeginImageContextWithOptions(targetSize, false, UIScreen.main.scale)
        image.draw(in: CGRect(origin: .zero, size: targetSize))
        let scaledImage = UIGraphicsGetImageFromCurrentImageContext() ?? image
        UIGraphicsEndImageContext()
        map[data.textDefaul] = scaledImage
    }
    return map
}()

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
    // 直接用字典匹配,无需遍历数组
    guard let matchedImage = textMapping[text] else {
        return true
    }
    let attachment = NSTextAttachment()
    attachment.image = matchedImage
    // 提前设置固定尺寸,避免渲染时实时计算,y值调整适配字体基线
    attachment.bounds = CGRect(x: 0, y: -4, width: 20, height: 20)
    let attString = NSAttributedString(attachment: attachment)
    // 修改文本前记录光标位置,修改后手动恢复避免光标跳变
    let targetSelectedRange = NSRange(location: range.location + 1, length: 0)
    textView.textStorage.replaceCharacters(in: range, with: attString)
    textView.selectedRange = targetSelectedRange
    // 手动修改完成后返回false,阻止系统默认插入逻辑
    return false
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.16 01:54:04