如何在不减慢过程的情况下使用颜色为文本着色?

How to use colour for text colouring without slowing down the process?

我发现字符串着色的时间取决于使用了多少不同的 NSColors。在下面的代码中,如果我对三种情况只使用一种颜色,那么文本着色过程比对这三种情况使用三种不同颜色的情况快 3 倍,每种情况每种颜色。为什么 ?有没有办法不减慢三种不同颜色的着色速度?

for i in 0..<arrayOfNSRangesForA.count
{
    textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForA[i])
}

for i in 0..<arrayOfNSRangesForT.count
{
   textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForT[i])
}

for i in 0..<arrayOfNSRangesForC.count
{
    textFromStorage.addAttribute(NSForegroundColorAttributeName, value: NSColor.green, range: arrayOfNSRangesForC[i])
}

更新 我又发现了一件坏事。当我将颜色从 NSForegroundColorAttributeName 更改为 NSBackgroundColorAttributeName 时,运行 时间显着增加 - 10 倍。对于 20 000 个字符,它是一种颜色,NSForegroundColorAttributeName- 1 秒,NSBackgroundColorAttributeName- 10 秒;如果三种颜色 - 相应地为 3 秒和 30 秒。对我来说,这是 Swift 非常糟糕的功能!!! DNA(ATGC 序列)着色无法正常工作,因为 DNA 的长度有数千个 A、T、G、C 字符!

更新 在评论中,我建议只为文本的可见部分着色。我已经尝试过这种方法,与我以标准方式所做的相比,即使是较短的文本也更糟糕。因此,我为文本的可见部分设置了 NSRange 文本,并在滚动时通过使用通知在滚动时动态着色。这是一个糟糕的方法。

您是否尝试过使用 ciColor 而不是属性? ciColors 可与文本、图像和背景一起使用。

你可以这样试试:

txtField.textColor?.ciColor.red

最大的障碍是在文本视图中布置所有这些属性字符。着色 DNA 序列需要最少的时间。无需编写自己的布局管理器或文本存储 class,您可以采用 divide-and-conquer 方法,一次对文本视图进行着色:

@IBOutlet var textView: NSTextView!
var dnaSequence: String!
var attributedDNASequence: NSAttributedString!

@IBAction func colorize(_ sender: Any) {
    self.dnaSequence = "ACGT" // your plaintext DNA sequence
    self.attributedDNASequence = self.makeAttributedDNASequence()

    // Rendering long string with the same attributes throughout is extremely fast
    self.textView.textStorage?.setAttributedString(NSAttributedString(string: dnaSequence))

    let step = 10_000   // colorize 10k characters at a time
    let delay = 0.2     // delay between each render
    for (i, location) in stride(from: 0, to: self.dnaSequence.characters.count, by: step).enumerated() {
        let length = min(step, self.dnaSequence.characters.count - location)
        let range = NSMakeRange(location, length)

        // Since we are modifying the textStorage of a GUI object (NSTextView)
        // we should do it on the main thread
        DispatchQueue.main.asyncAfter(deadline: .now() + (delay * Double(i))) {
            let subtext = self.attributedDNASequence.attributedSubstring(from: range)

            print("Replacing text in range \(location) to \(location + length)")
            self.textView.textStorage?.replaceCharacters(in: range, with: subtext)
        }
    }
}


// MARK: -
var colorA = NSColor.red
var colorC = NSColor.green
var colorG = NSColor.blue
var colorT = NSColor.black

func makeAttributedDNASequence() -> NSAttributedString {
    let attributedText = NSMutableAttributedString(string: dnaSequence)
    var index = dnaSequence.startIndex
    var color: NSColor!

    for i in 0..<dnaSequence.characters.count {
        switch dnaSequence[index] {
        case "A":
            color = colorA
        case "C":
            color = colorC
        case "G":
            color = colorG
        case "T":
            color = colorT
        default:
            color = NSColor.black
        }

        attributedText.addAttribute(NSForegroundColorAttributeName, value: color, range: NSMakeRange(i,1))
        index = dnaSequence.index(after: index)
    }

    return attributedText
}

诀窍是让应用程序尽可能地响应,这样用户就不会意识到事情仍在后台进行。使用小 delay(<= 0.3 秒)我无法足够快地滚动鼠标以在所有内容都被着色(100k 个字符)之前到达文本视图的末尾。

在 10 万个字符的测试中,彩色字符串第一次出现在文本视图中需要 0.7 秒,而不是一次完成所有操作时的 7 秒。