Ios 使用NSAttributedString在UITextView中为文本着色非常慢
我在Ios 使用NSAttributedString在UITextView中为文本着色非常慢,ios,swift,multithreading,textview,nsattributedstring,Ios,Swift,Multithreading,Textview,Nsattributedstring,我在UITextView上制作了一个简单的代码查看器/编辑器,因此我想给一些关键字(变量、函数等)上色,以便像在IDE中一样轻松查看。我正在使用NSAttributedString来执行此操作,并在循环中使用函数apply(…)在范围内着色(见下文)。然而,当有很多单词需要着色时,它开始变得非常慢,并且干扰键盘(在模拟器上不是那么多,但在实际设备上非常慢)。我想我可以使用线程来解决这个问题,但是当我在DispatchQueue.global().async{…}中运行apply函数时,它根本不会
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
}
这更多的是一些分析和建议,而不是完整的代码实现 当前代码完全重新扫描所有文本,并重新应用用户在文本视图中键入的每个字符的所有属性。显然,这是非常低效的 一个可能的改进是实现
shouldChangeTextInRange
委托。然后,您可以从现有的属性字符串开始,然后仅处理正在更改的范围。您可能需要在两侧处理一点文本,但这比重新处理整个文本要高效得多
也许你可以把两者结合起来。如果当前文本小于某个适当的大小,请进行完全扫描。一旦达到临界尺寸,进行部分更新
另一个需要考虑的问题是在后台扫描和创建属性字符串,但要使其可中断。每个文本更新您的取消和当前处理,然后重新开始。在用户停止输入足够长的时间以完成处理之前,不要使用新计算的属性文本更新文本视图
但我会利用仪器来分析代码。看看什么花的时间最多。是不是找到了文字?它正在创建属性字符串吗?是否经常设置文本视图的attributedText
属性
<>你也可以考虑深入研究核心文本。也许
UITextView
不太适合您的任务。这更多的是一些分析和建议,而不是完整的代码实现
当前代码完全重新扫描所有文本,并重新应用用户在文本视图中键入的每个字符的所有属性。显然,这是非常低效的
一个可能的改进是实现shouldChangeTextInRange
委托。然后,您可以从现有的属性字符串开始,然后仅处理正在更改的范围。您可能需要在两侧处理一点文本,但这比重新处理整个文本要高效得多
也许你可以把两者结合起来。如果当前文本小于某个适当的大小,请进行完全扫描。一旦达到临界尺寸,进行部分更新
另一个需要考虑的问题是在后台扫描和创建属性字符串,但要使其可中断。每个文本更新您的取消和当前处理,然后重新开始。在用户停止输入足够长的时间以完成处理之前,不要使用新计算的属性文本更新文本视图
但我会利用仪器来分析代码。看看什么花的时间最多。是不是找到了文字?它正在创建属性字符串吗?是否经常设置文本视图的attributedText
属性
<>你也可以考虑深入研究核心文本。也许
UITextView
不太适合您的任务。我有一个日志功能,可以记录我所做的所有服务调用,并可以在日志中搜索特定字符串。我在文本字段中显示文本,并在搜索时突出显示文本。我在正则表达式中使用below func,它并不慢。希望对你有帮助
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
}
}
我有一个日志功能,在其中我记录我所做的所有服务调用,并可以在日志中搜索特定字符串。我在文本字段中显示文本,并在搜索时突出显示文本。我在正则表达式中使用below func,它并不慢。希望对你有帮助
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
}
}
谢谢你的建议!非常感谢您花时间回答如此详细的问题。我进行完整扫描的原因是,例如,如果您声明了一个var,那么它必须在任何地方对该var进行着色(如果该var是全局的,并且在前面的函数中调用,则可能在文本层次结构中的声明之前)。我肯定需要做一些更智能的东西来处理这个问题,而且
shouldChangeTextInRange
会很有用。我喜欢中断以前对函数调用的解决方案,但是由于某些原因,我无法在后台运行apply。您需要更新您的问题,并提供有关使用后台线程的具体细节。也许一个简化版本的代码,使用一个属性化的字符串复制您的问题,并在后台通过文本视图进行更新,这将是一个很好的补充问题。关注该问题,而不考虑本问题中讨论的性能问题。感谢您的建议!非常感谢您花时间回答如此详细的问题。我进行完整扫描的原因是,例如,如果您声明了一个var,那么它必须在任何地方(可能甚至在