我在UITextView
之上创建了一个简单的代码查看器/编辑器,因此我想为一些关键字(变量,函数等)着色,以便它易于在IDE中查看。我使用NSAttributedString
执行此操作并使用循环中的函数apply(...)
在范围内着色(请参见下文)。然而,当有很多单词要着色时,它开始变得非常慢并且干扰键盘(在模拟器上没有那么多但在实际设备上它真的很慢)。我以为我可以使用线程来解决这个问题,但是当我在DispatchQueue.global().async {...}
中运行apply函数时,它根本没有任何颜色。通常,如果需要在主线程中运行某些UI调用,它将打印出错误/崩溃,因此我可以找到添加DispatchQueue.main.sync {...}
的位置,并且我已在各个地方尝试过它仍然无法正常工作。关于我如何解决这个问题的任何建议?
致电更新
func textViewDidChange(_ textView: UITextView) {
updateLineText()
}
更新功能
var wordToColor = [String:UIColor]()
func updateLineText() {
var newText = NSMutableAttributedString(string: content)
// some values are added to wordToColor here dynamically. This is quite fast and can be done asynchronously.
// when this is run asynchronously it doesn't color at all...
for word in wordToColor.keys {
newText = apply(string: newText, word: word)
}
textView.attributedText = newText
}
应用功能
func apply (string: NSMutableAttributedString, word: String) -> NSMutableAttributedString {
let range = (string.string as NSString).range(of: word)
return apply(string: string, word: word, range: range, last: range)
}
func apply (string: NSMutableAttributedString, word: String, range: NSRange, last: NSRange) -> NSMutableAttributedString {
if range.location != NSNotFound {
if (rangeCheck(range: range)) {
string.addAttribute(NSAttributedStringKey.foregroundColor, value: wordToColor[word], range: range)
if (range.lowerBound != 0) {
let index0 = content.index(content.startIndex, offsetBy: range.lowerBound-1)
if (content[index0] == ".") {
string.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.purple, range: range)
}
}
}
let start = last.location + last.length
let end = string.string.count - start
let stringRange = NSRange(location: start, length: end)
let newRange = (string.string as NSString).range(of: word, options: [], range: stringRange)
apply(string: string, word: word, range: newRange, last: range)
}
return string
}
答案 0 :(得分:1)
这将是一些分析和一些建议,而不是完整的代码实现。
您当前的代码会完全重新扫描所有文本,并为用户在文本视图中键入的每个字符重新应用所有属性。显然这是非常低效的。
一种可能的改进是实施shouldChangeTextInRange
代表。然后,您可以从现有的属性字符串开始,然后只处理要更改的范围。您可能需要在任何一方处理一些文本,但这比重新处理整个文本更有效。
你也许可以将两者结合起来。如果当前文本小于某个适当的大小,请执行完整扫描。达到临界尺寸后,进行部分更新。
另一个考虑因素是在后台进行所有扫描和创建属性字符串,但要使其可中断。每个文本都会更新您的取消和当前处理并重新开始。实际上,请不要使用新计算的属性文本更新文本视图,直到用户停止输入足够长的时间来完成处理。
但我会使用仪器并分析代码。看看它花了最多的时间。它找到了这些字吗?它是否创建了属性字符串?是否经常设置文本视图的attributedText
属性?
您也可以考虑深入了解Core Text。也许UITextView
并不适合你的任务。
答案 1 :(得分:1)
我有一个记录器功能,我可以记录我所做的所有服务调用,并可以搜索该日志中的特定字符串。我在文本字段中显示文本,并在搜索时突出显示文本。我使用下面的func与Regex,它不慢。希望它可以帮到你。
func searchText(searchString: String) {
guard let baseString = loggerTextView.text else {
return
}
let attributed = NSMutableAttributedString(string: baseString)
do {
let regex = try! NSRegularExpression(pattern: searchString,options: .caseInsensitive)
for match in regex.matches(in: baseString, options: NSRegularExpression.MatchingOptions(), range: NSRange(location: 0, length: baseString.count)) as [NSTextCheckingResult] {
attributed.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
attributed.addAttribute(NSFontAttributeName, value: UIFont.regularFont(ofSize: 14.0), range: NSRange(location: 0, length: attributed.string.count))
self.loggerTextView.attributedText = attributed
}
}