forked from shadowfacts/Tusker
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 text: Binding<String>
|
||||||
var didChange: (UITextView) -> Void
|
var didChange: (UITextView) -> Void
|
||||||
var uiState: ComposeUIState
|
var uiState: ComposeUIState
|
||||||
|
private var caretScrollPositionAnimator: UIViewPropertyAnimator?
|
||||||
|
|
||||||
init(text: Binding<String>, uiState: ComposeUIState, didChange: @escaping (UITextView) -> Void) {
|
init(text: Binding<String>, uiState: ComposeUIState, didChange: @escaping (UITextView) -> Void) {
|
||||||
self.text = text
|
self.text = text
|
||||||
|
@ -186,6 +187,54 @@ struct MainComposeWrappedTextView: UIViewRepresentable {
|
||||||
func textViewDidChange(_ textView: UITextView) {
|
func textViewDidChange(_ textView: UITextView) {
|
||||||
text.wrappedValue = textView.text
|
text.wrappedValue = textView.text
|
||||||
didChange(textView)
|
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) {
|
@objc func formatButtonPressed(_ sender: UIBarButtonItem) {
|
||||||
|
|
Loading…
Reference in New Issue