diff --git a/Sources/Runestone/TextView/TextInput/TextInputView.swift b/Sources/Runestone/TextView/TextInput/TextInputView.swift index 0b86df038..eabc2f9b4 100644 --- a/Sources/Runestone/TextView/TextInput/TextInputView.swift +++ b/Sources/Runestone/TextView/TextInput/TextInputView.swift @@ -414,7 +414,7 @@ final class TextInputView: UIView, UITextInput { var contentSize: CGSize { return layoutManager.contentSize } - private(set) var selectedRange: NSRange? { + var selectedRange: NSRange? { didSet { if selectedRange != oldValue { layoutManager.selectedRange = selectedRange diff --git a/Sources/Runestone/TextView/TextView.swift b/Sources/Runestone/TextView/TextView.swift index 017d7672e..ec5e473eb 100644 --- a/Sources/Runestone/TextView/TextView.swift +++ b/Sources/Runestone/TextView/TextView.swift @@ -987,8 +987,10 @@ private extension TextView { extension TextView: TextInputViewDelegate { func textInputViewWillBeginEditing(_ view: TextInputView) { isEditing = !willBeginEditingFromNonEditableTextInteraction - if textInputView.selectedTextRange == nil { - textInputView.selectedTextRange = IndexedRange(location: 0, length: 0) + // If a developer is programmatically calling becomeFirstresponder() then we might not have a selected range. + // We set the selectedRange instead of the selectedTextRange to avoid invoking any delegates. + if textInputView.selectedRange == nil { + textInputView.selectedRange = NSRange(location: 0, length: 0) } // Ensure selection is laid out without animation. UIView.performWithoutAnimation {