UITextView如何禁用滚动同时保留多行功能?
解决UITextView禁用滚动但保留多行输入的问题
这个坑我之前踩过好几次!很多人刚上手UITextView都会遇到:设置textView.isScrollEnabled = false后,本以为只是禁用滚动,结果连多行输入/显示的能力也没了。其实核心原因是关闭滚动后,UITextView会失去自动调整自身高度来适配内容的逻辑,超出默认高度的内容就被截断了。下面给你几个靠谱的解决方案:
方法一:通过代理动态调整高度(最常用)
这个方法适合快速实现,不需要子类化,只需要利用UITextView的代理方法实时计算内容高度,更新对应的约束或frame。
步骤:
- 先把
isScrollEnabled设为false,并设置代理 - 给UITextView添加一个高度约束的引用(用AutoLayout的话)
- 在
textViewDidChange代理方法里计算内容高度,更新约束
Swift代码示例:
// 先在ViewController里定义高度约束的引用 @IBOutlet weak var textViewHeightConstraint: NSLayoutConstraint! override func viewDidLoad() { super.viewDidLoad() // 基础设置 textView.isScrollEnabled = false textView.delegate = self // 初始化时计算一次高度,避免初始显示异常 updateTextViewHeight() } // 封装更新高度的方法 private func updateTextViewHeight() { let fixedWidth = textView.frame.size.width // 计算适配内容的最小高度 let contentSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)) textViewHeightConstraint.constant = contentSize.height } // 实现UITextView代理 extension ViewController: UITextViewDelegate { func textViewDidChange(_ textView: UITextView) { updateTextViewHeight() } }
小提示:
- 如果不用AutoLayout,直接修改
textView.frame.size.height也可以,但AutoLayout是更推荐的方式 - 记得初始化时调用一次高度更新,不然刚加载的文本可能显示不全
方法二:子类化UITextView重写intrinsicContentSize(更优雅)
如果你的项目里多个地方需要这个功能,子类化是更简洁的方案,复用性强。通过重写intrinsicContentSize,让UITextView自己计算合适的高度,不需要手动维护约束。
Swift代码示例:
class AutoHeightTextView: UITextView { override var intrinsicContentSize: CGSize { // 计算适配内容的高度 let contentSize = self.sizeThatFits(CGSize(width: self.frame.width, height: CGFloat.greatestFiniteMagnitude)) // 返回宽度自适应,高度为内容高度 return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height) } override func layoutSubviews() { super.layoutSubviews() // 当布局变化时,重新计算intrinsicContentSize if bounds.size != intrinsicContentSize { invalidateIntrinsicContentSize() } } }
使用方式:
- 在Storyboard里把UITextView的Class改成
AutoHeightTextView - 设置
isScrollEnabled = false - 给TextView添加左右上下的约束(不要加固定高度约束)
- 运行后它会自动根据内容调整高度,支持多行输入且不会滚动
方法三:Objective-C版本实现
如果你的项目用的是Objective-C,对应的实现如下:
动态调整高度的代码:
// 定义高度约束的引用 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *textViewHeightConstraint; - (void)viewDidLoad { [super viewDidLoad]; self.textView.scrollEnabled = NO; self.textView.delegate = self; // 初始化更新高度 [self updateTextViewHeight]; } - (void)updateTextViewHeight { CGSize fixedSize = CGSizeMake(self.textView.frame.size.width, CGFLOAT_MAX); CGSize contentSize = [self.textView sizeThatFits:fixedSize]; self.textViewHeightConstraint.constant = contentSize.height; } #pragma mark - UITextViewDelegate - (void)textViewDidChange:(UITextView *)textView { [self updateTextViewHeight]; }
子类化的Objective-C代码:
@interface AutoHeightTextView : UITextView @end @implementation AutoHeightTextView - (CGSize)intrinsicContentSize { CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)]; return CGSizeMake(UIViewNoIntrinsicMetric, contentSize.height); } - (void)layoutSubviews { [super layoutSubviews]; if (!CGSizeEqualToSize(self.bounds.size, self.intrinsicContentSize)) { [self invalidateIntrinsicContentSize]; } } @end
额外注意事项
- 如果你的UITextView有placeholder,记得同步调整placeholder的高度,避免遮挡
- 可以调整
textContainerInset和contentInset来优化内容的边距,避免内容紧贴边缘 - 不要同时设置
isScrollEnabled = false和固定高度约束,否则动态调整会失效
内容的提问来源于stack exchange,提问作者Md. Mostafizur Rahman
相关产品推荐
相关产品推荐

