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

时间:2017-01-31 16:47:30

标签: arrays swift nsattributedstring nscolor

我发现字符串着色的时间取决于使用了多少种不同的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的长度是数千个A,T,G,C字符,因此无法用DNA(ATGC序列)着色进行正常工作!

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

2 个答案:

答案 0 :(得分:2)

最大的障碍是在文本视图中列出所有这些属性字符。将DNA序列着色需要最少的时间。您可以采用分而治之的方法,而不是编写自己的布局管理器或文本存储类,而是通过一次一块地对文本视图进行着色:

@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(&lt; = 0.3秒),我无法快速滚动鼠标到达文本视图的末尾,然后才能对所有内容进行着色(100k字符)。

在一个100k字符的测试中,花了0.7秒直到彩色字符串首次出现在文本视图中,而不是7秒,如果我一次完成所有内容。

答案 1 :(得分:-1)

您是否尝试使用ciColor而不是属性? ciColors可用于文字,图片和背景。

您可以尝试这样:

txtField.textColor?.ciColor.red