UIScrollView中激活的UITextView自动滚动异常问题咨询
问题分析与解决方案
这是标准行为吗?
不是,这属于UITextView在处理宽于屏幕尺寸的非滚动文本视图时的适配逻辑bug。系统默认会尝试让UITextView的可视区域适配屏幕,但错误地将TextView的右边缘作为滚动目标,而非当前光标位置。
如何禁用自动滚动并让自定义逻辑生效?
核心思路是先阻断UITextView内部的自动滚动触发,再通过代理方法实现自定义的光标位置滚动。
1. 重写UITextView的scrollRangeToVisible:方法
系统内部会调用这个方法来触发TextView的自动滚动,重写为空实现即可完全阻断默认行为:
Objective-C 实现
@interface CustomTextView : UITextView @end @implementation CustomTextView - (void)scrollRangeToVisible:(NSRange)range { // 空实现,禁止UITextView内部自动滚动 } @end
Swift 实现
class CustomTextView: UITextView { override func scrollRangeToVisible(_ range: NSRange) { // 空实现,禁用默认滚动逻辑 } }
2. 实现自定义光标滚动逻辑
在UITextViewDelegate的textViewDidChangeSelection:方法中,获取光标对应的矩形区域,转换到外层UIScrollView的坐标系后,调用scrollRectToVisible:实现精准滚动:
Objective-C 实现
- (void)textViewDidChangeSelection:(UITextView *)textView { if (!textView.selectedTextRange) return; // 获取光标所在的矩形 CGRect caretRect = [textView firstRectForRange:textView.selectedTextRange]; // 转换坐标到外层UIScrollView CGRect scrollViewRect = [textView convertRect:caretRect toView:self.mainScrollView]; // 添加内边距,避免光标贴边显示 UIEdgeInsets visibleInsets = UIEdgeInsetsMake(20, 20, 20, 20); scrollViewRect = UIEdgeInsetsInsetRect(scrollViewRect, visibleInsets); [self.mainScrollView scrollRectToVisible:scrollViewRect animated:YES]; }
Swift 实现
func textViewDidChangeSelection(_ textView: UITextView) { guard let selectedRange = textView.selectedTextRange else { return } var caretRect = textView.firstRect(for: selectedRange) // 转换坐标到外层UIScrollView let scrollViewRect = textView.convert(caretRect, to: mainScrollView) // 添加内边距优化显示效果 let visibleInsets = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20) let adjustedRect = scrollViewRect.inset(by: visibleInsets) mainScrollView.scrollRectToVisible(adjustedRect, animated: true) }
3. 额外优化点
- 确保你的CustomTextView的
isScrollEnabled属性设置为NO(Objective-C)或false(Swift),避免系统残留的滚动逻辑干扰; - 在
textViewDidBeginEditing:方法中也执行一次上述滚动逻辑,确保TextView成为第一响应者时,光标能立即处于可视区域。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

