如何在 swift 中逐行突出显示 UITextView 的文本?

How to highlight a UITextView's text line by line in swift?

我试图逐行突出显示 UITextView 中的文本。我想遍历每一行并突出显示那一行供用户查看,然后我想删除突出显示效果以为下一行做准备。我已经尝试创建解决方案但未能成功,这是我现在最好的机会。

这是我到目前为止一直在做的一些工作,由于某种原因,它目前用“NSBackgroundColor 1101”填充了 UITextView,我不知道为什么会这样。

func highlight() {
        let str = "This is\n some placeholder\n text\nwith newlines."
        var newStr = NSMutableAttributedString(string: "")
        var arr:[String] = str.components(separatedBy: "\n")

        var attArr:[NSMutableAttributedString] = []

        for i in 0..<arr.count {
            attArr.append(NSMutableAttributedString(string: arr[i]))
        }

        for j in 0..<attArr.count {
            let range = NSMakeRange(0, attArr[j].length)
            attArr[j].addAttribute(.backgroundColor, value: UIColor.yellow, range: range)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5){
                for m in 0..<attArr.count {
                    newStr = NSMutableAttributedString(string: "\(attArr[m])\n")
                    self.textView.attributedText = newStr

                }
            }
            attArr[j].removeAttribute(.backgroundColor, range: range)
            //remove from texview here
        }
    }

如您所见,该算法应该剥离 textView 文本并将其放入一个数组中,方法是用新行分隔符分隔每一行。 接下来要做的是创建一个数组,其中填充了相同的文本但作为可变属性字符串开始添加高亮属性。 每次出现突出显示的行时,都会有一个小的延迟,直到下一行开始突出显示。如果有人可以帮助我或为我指明正确的方向以开始正确实施,那将大有帮助,

谢谢!

所以你想要这个:

您需要文本视图的内容始终是完整的字符串,突出显示一行,但您的代码将其设置为仅突出显示的行。您的代码还安排所有高亮显示同时发生 (.now() + 0.5),而不是在不同时间发生。

这是我的建议:

  1. 创建一个范围数组,每行一个范围。

  2. 使用该数组修改文本视图的 textStorage,根据需要删除和添加 .backgroundColor 属性以突出显示和取消突出显示行。

  3. 当您突出显示第 n 行时,安排第 n+1 行的突出显示。这有两个好处:如果需要的话,提前取消动画会更容易和更有效,如果需要的话,让动画无限重复会更容易。

我使用这个 playground 创建了上面的演示:

import UIKit
import PlaygroundSupport

let text = "This is\n some placeholder\n text\nwith newlines."
let textView = UITextView(frame: CGRect(x: 0, y:0, width: 200, height: 100))
textView.backgroundColor = .white
textView.text = text

let textStorage = textView.textStorage

// Use NSString here because textStorage expects the kind of ranges returned by NSString,
// not the kind of ranges returned by String.
let storageString = textStorage.string as NSString
var lineRanges = [NSRange]()
storageString.enumerateSubstrings(in: NSMakeRange(0, storageString.length), options: .byLines, using: { (_, lineRange, _, _) in
    lineRanges.append(lineRange)
})

func setBackgroundColor(_ color: UIColor?, forLine line: Int) {
    if let color = color {
        textStorage.addAttribute(.backgroundColor, value: color, range: lineRanges[line])
    } else {
        textStorage.removeAttribute(.backgroundColor, range: lineRanges[line])
    }
}

func scheduleHighlighting(ofLine line: Int) {
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
        if line > 0 { setBackgroundColor(nil, forLine: line - 1) }
        guard line < lineRanges.count else { return }
        setBackgroundColor(.yellow, forLine: line)
        scheduleHighlighting(ofLine: line + 1)
    }
}

scheduleHighlighting(ofLine: 0)

PlaygroundPage.current.liveView = textView