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

iOS UITextView点击可变属性字符串对应句子变色及性能优化问题

问题1:点击获取整句范围并变色的实现

你可以直接在现有的textTapped方法中实现逻辑,步骤如下:

  1. 计算点击位置对应在textView文本中的字符索引
  2. 匹配到该索引所属的句子/段落范围
  3. 修改对应范围的富文本属性即可
    示例代码:
// 先新增一个全局属性存储每个段落的范围,提前记录避免实时枚举
var paragraphRanges = [NSRange]()

@objc func textTapped(_ sender:UITapGestureRecognizer) {
    guard let textView = sender.view as? UITextView else { return }
    // 获取点击位置对应的字符索引
    let tapLocation = sender.location(in: textView)
    let charIndex = textView.layoutManager.characterIndex(for: tapLocation, in: textView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
    guard charIndex != NSNotFound else { return }
    
    // 方式1:如果你要按系统语义的句子匹配,用枚举句子的方法
    guard let text = textView.text else { return }
    let fullRange = NSRange(location: 0, length: text.utf16.count)
    var targetRange = NSRange()
    text.enumerateSubstrings(in: Range(fullRange, in: text)!, options: .bySentences) { substring, range, _, stop in
        if NSLocationInRange(charIndex, NSRange(range, in: text)) {
            targetRange = NSRange(range, in: text)
            stop.pointee = true
        }
    }
    
    // 方式2:更符合你业务场景的方案,直接匹配提前记录的段落范围(推荐)
    // for range in paragraphRanges {
    //     if NSLocationInRange(charIndex, range) {
    //         targetRange = range
    //         break
    //     }
    // }
    
    guard targetRange.location != NSNotFound else { return }
    // 修改整句/整段颜色
    let mutableAttr = NSMutableAttributedString(attributedString: textView.attributedText)
    mutableAttr.addAttribute(.foregroundColor, value: UIColor.red, range: targetRange)
    textView.attributedText = mutableAttr
}

需要提前记录段落范围的话,在你生成富文本的循环里追加如下逻辑:

Array(1..<50).forEach {
    let start = attributedString.length
    // 原有添加文本、添加附件的逻辑不变
    // ...
    let end = attributedString.length
    paragraphRanges.append(NSRange(location: start, length: end - start))
}
问题2:性能更优的UI实现方案
  • 段落数量在100以内的话,现有UITextView方案性能足够,不需要额外调整
  • 段落数量更多、内容更长的场景,推荐使用UITableView复用方案:
    • 每个带编号的段落对应一个TableViewCell,cell内部分别用UILabel/UIImageView承载文本和编号,不需要生成NSTextAttachment,内存占用更低
    • 关闭TableView的分隔线,开启自动行高,滚动时系统会自动复用cell,不会一次性加载所有内容到内存,滚动流畅度远高于单个UITextView加载超长文本
  • 如果需要保留跨段落选中文本等整段文本交互特性,可以用TextKit的区域渲染能力,仅渲染当前可视区域+上下少量预加载区域的文本,无需一次性加载全部内容。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 06:48:04