Ensure the cursor remains visible when composing posts
This commit is contained in:
parent
b8f169d0cd
commit
16b02edf87
|
@ -176,6 +176,7 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
|||
var text: Binding<String>
|
||||
var didChange: (UITextView) -> Void
|
||||
var uiState: ComposeUIState
|
||||
private var caretScrollPositionAnimator: UIViewPropertyAnimator?
|
||||
|
||||
init(text: Binding<String>, uiState: ComposeUIState, didChange: @escaping (UITextView) -> Void) {
|
||||
self.text = text
|
||||
|
@ -186,6 +187,54 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
|||
func textViewDidChange(_ textView: UITextView) {
|
||||
text.wrappedValue = textView.text
|
||||
didChange(textView)
|
||||
|
||||
ensureCursorVisible()
|
||||
}
|
||||
|
||||
private func ensureCursorVisible() {
|
||||
guard let textView = textView,
|
||||
textView.isFirstResponder,
|
||||
let range = textView.selectedTextRange,
|
||||
let scrollView = findParentScrollView() else {
|
||||
return
|
||||
}
|
||||
|
||||
// We use a UIViewProperty animator to change the scroll view position so that we can store the currently
|
||||
// running one on the Coordinator. This allows us to cancel the running one, preventing multiple animations
|
||||
// from attempting to change the scroll view offset simultaneously, causing it to jitter around. This can
|
||||
// happen if the user is pressing return and quickly creating many new lines.
|
||||
|
||||
if let existing = caretScrollPositionAnimator {
|
||||
existing.stopAnimation(true)
|
||||
}
|
||||
|
||||
let cursorRect = textView.caretRect(for: range.start)
|
||||
var rectToMakeVisible = textView.convert(cursorRect, to: scrollView)
|
||||
|
||||
// move Y position of the rect that will be made visible down by the cursor's height so that there's
|
||||
// some space between the bottom of the line of text being edited and the top of the keyboard
|
||||
rectToMakeVisible.origin.y += cursorRect.height
|
||||
|
||||
let animator = UIViewPropertyAnimator(duration: 0.1, curve: .linear) {
|
||||
scrollView.scrollRectToVisible(rectToMakeVisible, animated: false)
|
||||
}
|
||||
self.caretScrollPositionAnimator = animator
|
||||
animator.startAnimation()
|
||||
}
|
||||
|
||||
private func findParentScrollView() -> UIScrollView? {
|
||||
guard let textView = textView else { return nil }
|
||||
|
||||
var current: UIView = textView
|
||||
while let superview = current.superview {
|
||||
if let scrollView = superview as? UIScrollView {
|
||||
return scrollView
|
||||
} else {
|
||||
current = superview
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@objc func formatButtonPressed(_ sender: UIBarButtonItem) {
|
||||
|
|
Loading…
Reference in New Issue