如何解决UITextView自动滚动冲突及键盘遮挡问题?
Hey there! That auto-scroll behavior in UITextView sure can be a pain when you're trying to handle custom scrolling for keyboard occlusion. Let's break down how to either disable that annoying auto-scroll or switch to a more reliable solution altogether.
Option 1: Disable UITextView's Auto-Scroll
The built-in auto-scroll triggers when UITextView calls scrollRangeToVisible(_:). To take control, subclass UITextView and add a flag to toggle this behavior:
class CustomTextView: UITextView { var shouldBlockAutoScroll = false override func scrollRangeToVisible(_ range: NSRange) { // Only let the system handle scrolling if we haven't blocked it if !shouldBlockAutoScroll { super.scrollRangeToVisible(range) } } }
Then update your scrollToVisible() function to block auto-scroll during your custom scroll, then re-enable it after the animation finishes:
func scrollToVisible() { guard let cursorStart = self.EditArea.selectedTextRange?.start else { return } // Block system auto-scroll temporarily (self.EditArea as? CustomTextView)?.shouldBlockAutoScroll = true let cursorRect = self.EditArea.caretRect(for: cursorStart) let cursortop = self.EditArea.convert(cursorRect.origin, to: self.view) let cursorbottom = cursortop.y + cursorRect.height let bottom = UIScreen.main.bounds.size.height - self.EditArea.textContainerInset.bottom var contentOffset = self.EditArea.contentOffset if cursortop.y <= 85 { contentOffset.y -= (85 - cursortop.y) self.EditArea.setContentOffset(contentOffset, animated: true) } else if cursorbottom.y >= bottom { contentOffset.y += (cursorbottom.y - bottom) self.EditArea.setContentOffset(contentOffset, animated: true) } // Re-enable auto-scroll after the animation completes DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { (self.EditArea as? CustomTextView)?.shouldBlockAutoScroll = false } }
Option 2: A More Reliable Keyboard Occlusion Fix (No Custom Scrolling Needed)
Instead of fighting with UITextView's auto-scroll, use Apple's recommended approach: adjust the text view's content inset when the keyboard appears. This lets the system handle scrolling automatically, and it's way less error-prone.
Step 1: Register Keyboard Notifications
Add this to your view controller's viewDidLoad():
override func viewDidLoad() { super.viewDidLoad() // Listen for keyboard show/hide events NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil ) }
Step 2: Handle Keyboard Events
Add these callback functions to adjust the content inset:
@objc private func keyboardWillShow(_ notification: Notification) { guard let keyboardFrame = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? CGRect else { return } let keyboardHeight = keyboardFrame.height // Expand the text view's content inset to account for the keyboard EditArea.contentInset = UIEdgeInsets( top: 0, left: 0, bottom: keyboardHeight, right: 0 ) EditArea.scrollIndicatorInsets = EditArea.contentInset } @objc private func keyboardWillHide(_ notification: Notification) { // Reset the content inset when the keyboard disappears EditArea.contentInset = UIEdgeInsets.zero EditArea.scrollIndicatorInsets = UIEdgeInsets.zero }
Step 3: Clean Up Notifications
Don't forget to remove the observer when your view controller is deallocated:
deinit { NotificationCenter.default.removeObserver(self) }
Bonus: Ensure Cursor Stays Visible
If you still want to guarantee the cursor never gets hidden after selection changes, add this UITextView delegate method (make sure your view controller conforms to UITextViewDelegate):
func textViewDidChangeSelection(_ textView: UITextView) { guard let selectedRange = textView.selectedTextRange else { return } // Let the system scroll the cursor into view (now that content inset is adjusted) textView.scrollRangeToVisible(textView.textRange(from: selectedRange.start, to: selectedRange.start)!.nsRange) }
内容的提问来源于stack exchange,提问作者Sidney Liu

