You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决UITextView自动滚动冲突及键盘遮挡问题?

Fixing UITextView Auto-Scroll Interference & Keyboard Occlusion

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:36:33