如何正确实现UITextView高度随内容自适应调整?
问题根因
两个方案的核心错误都是错误修改了translatesAutoresizingMaskIntoConstraints属性,打断了Auto Layout的正常布局流程:
第一种方案问题
代码如下:
func textViewDidChange(_ textView: UITextView) { self.comments.translatesAutoresizingMaskIntoConstraints = true self.comments.sizeToFit() self.comments.isScrollEnabled = false }
直接调用sizeToFit()时,因为没有给TextView固定宽度,系统会按内容计算最小可容纳尺寸,最终宽度被压缩到仅能容纳单个字符,就会出现每行只能输入1个字符的问题。
第二种方案问题
代码如下:
func textViewDidChange(_ textView: UITextView) { self.comments.translatesAutoresizingMaskIntoConstraints = true self.comments.isScrollEnabled = false let fixedWidth = passedOut.frame.size.width let newSize = comments.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude)) comments.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height) }
viewDidLoad阶段视图尚未完成约束布局,此时passedOut的宽度还未计算为约束设定的正确值,提前把translatesAutoresizingMaskIntoConstraints设为true后,系统会用当前错误的宽度生成frame,导致首屏TextView宽度偏窄。等开始输入触发回调时,视图已经完成首轮布局,passedOut宽度正确,计算出的TextView尺寸就恢复正常了。
viewDidLoad阶段异常效果如下:
正确实现方案
全程使用Auto Layout控制尺寸,不要随意修改translatesAutoresizingMaskIntoConstraints为true(纯约束布局下这个属性保持默认false即可),实现步骤:
- 给
comments这个UITextView设置和passedOut一致的leading、trailing约束,再加一个高度约束,将其关联为属性commentsHeightConstraint - 完成基础初始化配置:
override func viewDidLoad() { super.viewDidLoad() comments.isScrollEnabled = false // 其余业务配置 }
- 首屏布局完成后更新一次高度,解决初始加载尺寸不对的问题:
override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() updateCommentsHeight() }
- 输入内容时实时更新高度:
func textViewDidChange(_ textView: UITextView) { updateCommentsHeight() }
- 封装高度计算方法:
func updateCommentsHeight() { let calculateSize = CGSize(width: comments.bounds.width, height: .greatestFiniteMagnitude) let targetHeight = comments.sizeThatFits(calculateSize).height // 如果需要设置最小高度,可改为 commentsHeightConstraint.constant = max(targetHeight, 最小高度值) commentsHeightConstraint.constant = targetHeight view.layoutIfNeeded() }
- 该方案完全通过约束控制尺寸,不会出现宽度计算错误、首屏布局异常的问题
- 不要在输入回调中切换布局模式(约束/frame),会导致布局状态混乱
内容的提问来源于stack exchange,提问作者Selva Balaji
相关产品推荐
相关产品推荐

