Swift中如何实现UITextView文本的逐行高亮?
解决UITextView逐行高亮的问题
我看了你的代码,问题主要出在这几个地方:
- 循环里的
DispatchQueue.main.asyncAfter会让所有延迟任务几乎同时触发(循环执行太快,每个任务都只延迟0.5秒),而且每次都把newStr设置成单个行的属性字符串,还直接把attArr[m]转成普通字符串——这就是你看到NSBackgroundColor 1101的原因,属性字符串直接转字符串会输出它的内部属性信息。 - 你在设置延迟任务后立刻移除了高亮属性,等延迟任务执行时,那行的高亮已经被删掉了,自然看不到效果。
- 没有维护整个文本的完整属性,每次只替换成单行,导致文本显示混乱。
下面是修正后的实现思路和代码:
正确的实现步骤
- 先把完整文本转换成全局的可变属性字符串,这样我们可以持续修改它的属性而不用每次重新拼接。
- 计算每一行在整个字符串中的实际范围(要注意换行符的位置,避免把换行符也高亮)。
- 用串行的延迟操作(递归调用+延迟),依次处理每一行:先取消上一行的高亮,再给当前行添加高亮,最后更新UITextView的内容。
完整代码示例
class ViewController: UIViewController { @IBOutlet weak var textView: UITextView! // 全局保存完整的可变属性字符串,方便持续修改 private var fullAttributedText: NSMutableAttributedString! // 记录上一次高亮的行范围,用于取消高亮 private var lastHighlightedRange: NSRange? // 当前处理的行索引 private var currentLineIndex = 0 // 存储每一行在完整字符串中的范围 private var lineRanges: [NSRange] = [] override func viewDidLoad() { super.viewDidLoad() setupTextViewContent() } private func setupTextViewContent() { let str = "This is\n some placeholder\n text\nwith newlines." fullAttributedText = NSMutableAttributedString(string: str) textView.attributedText = fullAttributedText // 计算每一行的实际范围(不包含换行符) let lines = str.components(separatedBy: "\n") var currentLocation = 0 for line in lines { let lineLength = line.count lineRanges.append(NSMakeRange(currentLocation, lineLength)) // 跳过换行符的位置(最后一行没有换行符) currentLocation += lineLength + (line == lines.last ? 0 : 1) } } // 调用这个方法开始逐行高亮 func startLineByLineHighlight() { currentLineIndex = 0 lastHighlightedRange = nil highlightNextLine() } private func highlightNextLine() { // 所有行处理完,取消最后一行的高亮 guard currentLineIndex < lineRanges.count else { if let range = lastHighlightedRange { fullAttributedText.removeAttribute(.backgroundColor, range: range) textView.attributedText = fullAttributedText } return } // 1. 取消上一行的高亮 if let lastRange = lastHighlightedRange { fullAttributedText.removeAttribute(.backgroundColor, range: lastRange) } // 2. 高亮当前行 let currentRange = lineRanges[currentLineIndex] fullAttributedText.addAttribute(.backgroundColor, value: UIColor.yellow, range: currentRange) lastHighlightedRange = currentRange // 3. 更新UITextView显示 textView.attributedText = fullAttributedText // 4. 延迟0.5秒处理下一行 currentLineIndex += 1 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.highlightNextLine() } } }
代码说明
- 在
setupTextViewContent里提前计算好每一行的NSRange,确保后续能精准定位要高亮的文本。 startLineByLineHighlight是触发高亮的入口方法,调用它就开始逐行展示效果。highlightNextLine是核心逻辑:先清除上一行的高亮,再给当前行添加高亮,更新文本后递归调用自己处理下一行,直到所有行处理完毕。- 用递归+延迟的方式保证高亮操作是串行执行的,不会出现所有行同时高亮的问题。
这样就能实现你想要的逐行高亮、自动切换的效果,也不会再出现奇怪的属性字符串输出了。
内容的提问来源于stack exchange,提问作者electronicwall
相关产品推荐
相关产品推荐

