如何在不调用textViewDidChangeSelection的情况下设置NSAttributedString属性?

时间:2017-08-17 19:08:44

标签: ios swift uitextview nsattributedstring uitextviewdelegate

我正在尝试做什么:

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
}

当我在shouldChangeTextIn的{​​{1}}方法中调用此函数时效果很好,但只有在用户输入时才有效,我想在用户只更改选择时调用此函数{{ 1}}似乎很合适。

但是有一个问题:

当我将修改后的referencedString分配给UITextViewDelegate时,它将光标移动到文本的末尾(用保存光标位置修复它并在所有更改后再次设置它)。但问题是这两件事都在移动光标并再次调用textViewDidChangeSelection所以发生无限循环(UITextView正在调用我的textViewDidChangeSelection,这会修改光标位置,因此调用{{1再次)。

所以问题是:

我可以在不创建NSMutableAttributedString的情况下更改attributesString的属性(基本上只是颜色),并再次将其重新分配给UITextView.attributedText,以省略将光标移动到文本的末尾并返回吗?

或许有一种方法可以某种方式关闭它不应该移动那个光标或者让它暂时不调用代理?

2 个答案:

答案 0 :(得分:1)

经过长时间的战斗后,我最终得到了一些简单的解决方法,这在早些时候没有出现在我的脑海中。

在放置在setFocus函数中的textViewDidChangeSelection中,我将UITextViewDelegate设置为nil,在更改颜色和设置光标后,我再次分配代理。不是一个干净的解决方案,但至少有效。

答案 1 :(得分:0)

我为我的一个项目写了一个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
})