如何在不调用 textViewDidChangeSelection 的情况下设置 NSAttributedString 属性?
How to set NSAttributedString attributes without invoking textViewDidChangeSelection?
我想做什么:
UITextView
中有一个属性文本,当用户点击文本某处(不是选择文本,只是将闪烁的光标放在其他地方)时,光标周围的整个句子都会改变颜色。
我是如何尝试做到这一点的:
private func setFocus(boundarySymbols: CharacterSet) {
let mutableString = NSMutableAttributedString(attributedString: noteTextView.attributedText)
mutableString.beginEditing()
unfocusWholeText(mutableString: mutableString)
let selectedRange = noteTextView.selectedRange
guard let caretPosition = findCaretPosition() else { return }
guard let focusRange = findRangeToFocus(in: noteTextView.text, around: caretPosition, of: boundarySymbols) else { return }
text.addAttribute(NSForegroundColorAttributeName, value: ThemeManager.theme.note.text, range: focusRange)
mutableString.endEditing()
noteTextView.attributedText = mutableString
noteTextView.selectedRange = selectedRange
}
当我在 UITextViewDelegate
的 shouldChangeTextIn
方法中调用此函数时效果很好,但它仅在用户键入时有效,我想在用户仅更改选择时也调用此函数所以textViewDidChangeSelection
似乎非常合适。
但是有个问题:
当我将修改后的 attributedString 分配给 UITextView
时,它会将光标移动到文本的末尾(通过保存光标位置并在所有更改后重新设置来修复它)。但问题是这两个东西都在移动光标并且再次调用 textViewDidChangeSelection
所以无限循环发生(textViewDidChangeSelection
正在调用我的 setFocus
这是修改光标位置所以调用 textViewDidChangeSelection
再次)。
所以问题是:
我可以更改 attributedString 的属性(基本上只是颜色)而不创建 NSMutableAttributedString 并将其重新分配给 UITextView.attributedText 以省略将光标移动到文本末尾并返回吗?
或者也许有一种方法可以在不应该移动光标时以某种方式关闭它,或者让它暂时不调用委托?
我为我的其中一个项目写了一个 UITextField
扩展,可以让你这样做:
extension UITextField {
enum ShouldChangeCursor {
case incrementCursor
case preserveCursor
}
func preserveCursorPosition(withChanges mutatingFunction: (UITextPosition?) -> (ShouldChangeCursor)) {
//save the cursor positon
var cursorPosition: UITextPosition? = nil
if let selectedRange = self.selectedTextRange {
let offset = self.offset(from: self.beginningOfDocument, to: selectedRange.start)
cursorPosition = self.position(from: self.beginningOfDocument, offset: offset)
}
//make mutaing changes that may reset the cursor position
let shouldChangeCursor = mutatingFunction(cursorPosition)
//restore the cursor
if var cursorPosition = cursorPosition {
if shouldChangeCursor == .incrementCursor {
cursorPosition = self.position(from: cursorPosition, offset: 1) ?? cursorPosition
}
if let range = self.textRange(from: cursorPosition, to: cursorPosition) {
self.selectedTextRange = range
}
}
}
}
您可以使用它来更新属性文本而无需更改光标位置:
textField.preserveCursorPosition(withChanges: { _ in
textField.attributedText = ...
return .preserveCursor
})
经过长时间的斗争,我最终找到了我之前没有想到的简单解决方法。
在 setFocus
中放置在 textViewDidChangeSelection
函数中,我将 UITextViewDelegate
设置为 nil
,在更改颜色和设置光标后,我再次分配委托。不是一个干净的解决方案,但至少有效。
我想做什么:
UITextView
中有一个属性文本,当用户点击文本某处(不是选择文本,只是将闪烁的光标放在其他地方)时,光标周围的整个句子都会改变颜色。
我是如何尝试做到这一点的:
private func setFocus(boundarySymbols: CharacterSet) {
let mutableString = NSMutableAttributedString(attributedString: noteTextView.attributedText)
mutableString.beginEditing()
unfocusWholeText(mutableString: mutableString)
let selectedRange = noteTextView.selectedRange
guard let caretPosition = findCaretPosition() else { return }
guard let focusRange = findRangeToFocus(in: noteTextView.text, around: caretPosition, of: boundarySymbols) else { return }
text.addAttribute(NSForegroundColorAttributeName, value: ThemeManager.theme.note.text, range: focusRange)
mutableString.endEditing()
noteTextView.attributedText = mutableString
noteTextView.selectedRange = selectedRange
}
当我在 UITextViewDelegate
的 shouldChangeTextIn
方法中调用此函数时效果很好,但它仅在用户键入时有效,我想在用户仅更改选择时也调用此函数所以textViewDidChangeSelection
似乎非常合适。
但是有个问题:
当我将修改后的 attributedString 分配给 UITextView
时,它会将光标移动到文本的末尾(通过保存光标位置并在所有更改后重新设置来修复它)。但问题是这两个东西都在移动光标并且再次调用 textViewDidChangeSelection
所以无限循环发生(textViewDidChangeSelection
正在调用我的 setFocus
这是修改光标位置所以调用 textViewDidChangeSelection
再次)。
所以问题是:
我可以更改 attributedString 的属性(基本上只是颜色)而不创建 NSMutableAttributedString 并将其重新分配给 UITextView.attributedText 以省略将光标移动到文本末尾并返回吗?
或者也许有一种方法可以在不应该移动光标时以某种方式关闭它,或者让它暂时不调用委托?
我为我的其中一个项目写了一个 UITextField
扩展,可以让你这样做:
extension UITextField {
enum ShouldChangeCursor {
case incrementCursor
case preserveCursor
}
func preserveCursorPosition(withChanges mutatingFunction: (UITextPosition?) -> (ShouldChangeCursor)) {
//save the cursor positon
var cursorPosition: UITextPosition? = nil
if let selectedRange = self.selectedTextRange {
let offset = self.offset(from: self.beginningOfDocument, to: selectedRange.start)
cursorPosition = self.position(from: self.beginningOfDocument, offset: offset)
}
//make mutaing changes that may reset the cursor position
let shouldChangeCursor = mutatingFunction(cursorPosition)
//restore the cursor
if var cursorPosition = cursorPosition {
if shouldChangeCursor == .incrementCursor {
cursorPosition = self.position(from: cursorPosition, offset: 1) ?? cursorPosition
}
if let range = self.textRange(from: cursorPosition, to: cursorPosition) {
self.selectedTextRange = range
}
}
}
}
您可以使用它来更新属性文本而无需更改光标位置:
textField.preserveCursorPosition(withChanges: { _ in
textField.attributedText = ...
return .preserveCursor
})
经过长时间的斗争,我最终找到了我之前没有想到的简单解决方法。
在 setFocus
中放置在 textViewDidChangeSelection
函数中,我将 UITextViewDelegate
设置为 nil
,在更改颜色和设置光标后,我再次分配委托。不是一个干净的解决方案,但至少有效。