Search code examples
iosswiftuitextviewnsattributedstringuitextviewdelegate

How to set NSAttributedString attributes without invoking textViewDidChangeSelection?


What I'm trying to do:

There is an attributed text in UITextView, when user tap somewhere on text (not selecting text, just placing blinking cursor somewhere else) the whole sentence around cursor changes color.

How I tried to do this:

   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
}

Works great when I'm calling this function in shouldChangeTextIn method of UITextViewDelegate, but it works only when user is typing, I would like to call this also when user only changes selection so textViewDidChangeSelection seems to fit perfectly.

But there is a problem:

When I assign modified attributedString to UITextView it moves cursor to the end of text (fixed it with saving cursor position and setting it again after all changes). But the problem is that both of these things are moving cursor and that invokes textViewDidChangeSelection again so infinite loop happens (textViewDidChangeSelection is calling my setFocus which is modifing cursor position so is calling textViewDidChangeSelection again).

So the question is:

Can I change attributes (basically just color) of attributedString without creating NSMutableAttributedString and reassigning it to UITextView.attributedText again to omit moving cursor to end of the text and back?

Or maybe there is a way to somehow turn off moving that cursor around when it shouldn't or make it not calling delegate for a while?


Solution

  • After a long battle I ended up with simple workaround which didn't came to my mind earlier.

    In setFocus placed in textViewDidChangeSelection function I set UITextViewDelegate to nil and after changing colors and setting cursor I assign delegate again. Not a clean solution, but works at least.