I'm trying to do some stuff when user taps Enter button, so I have implemented the following delegates for UITextView:
// Delegate is called when text is gonna change
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
// Some behavior when user taps Enter
return true
}
// Some other code
return true
}
// Delegate is called when selection is changed
func textViewDidChangeSelection(_ textView: UITextView) {
// Some code
}
The problem is, textViewDidChangeSelection
is called twice when user taps Enter.
In second call, textView.selectedRange
is changed to the last character of text view. This produces a problem when user taps Enter after any line in the middle of text, because caret position is changed to end of text.
I've attached a reproducible example here so you can check, I'm not sure if the problem is in the delegates, or in the way I attached the delegate to the view.
To re-produce the problem, do the following scenario:
Write some lines, for example:
Line 1.
Bla bla bla
line 3
Go to the end of line 1.
Tap on Enter.
The new line is there in the right position, but the caret position is changed to the end of text view.
Notes:
EditorTextView.swift
only.OK finally, I found a solution for this weird behavior of delegation in the UITextView wrapper for SwiftUI.
So the entire issue was with setting the text again every time updateUIView
was called. That is not necessary and it caused a weird feedback loop with textViewDidChange
delegate method (that is where I set the selected range when the last character was \n
, which is the case for a new line).
I don't know exactly why does that happen, I just think it's a bug with how UIViewRepresentables
works under the hood.
So, to solve the issue, I had to move the following line from updateUIView
:
uiView.text = document
And add it to makeUIView
:
textView.text = document
This way, it will be bound permanently.