点击按钮添加动态高度UITextView,ScrollView内容尺寸适配问题
解决思路与实现方案
嘿,我来帮你搞定这个问题!你现在的核心痛点是嵌套的UITableView和外层UIScrollView的高度联动,还有UITextView的自动增高要层层传递下去。其实只要把每个环节的自动布局和动态更新逻辑理顺,就能完美解决,下面一步步来:
1. 先搞定UITextView的自动增高
首先每个单元格里的UITextView必须关闭滚动(isScrollEnabled = false),这样它才会根据内容自动撑开高度。然后我们需要监听它的内容变化,触发后续的高度更新:
自定义单元格代码
class TextViewCell: UITableViewCell, UITextViewDelegate { @IBOutlet weak var textView: UITextView! // 定义一个回调,用来通知外部内容变化了 var onHeightChanged: (() -> Void)? override func awakeFromNib() { super.awakeFromNib() textView.isScrollEnabled = false textView.delegate = self // 给TextView加个内边距,让文字不贴边 textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) } func textViewDidChange(_ textView: UITextView) { // 强制更新TextView布局,确保高度计算准确 textView.layoutIfNeeded() // 通知外部高度变化了 onHeightChanged?() } }
2. 让UITableView自动适配内容高度
因为你把UITableView的滚动关了,它就变成了一个“自适应高度的容器”。我们需要给它加一个高度约束,然后每次内容变化时更新这个约束的数值:
ViewController里的关键设置
首先在Storyboard里给UITableView加一个高度约束,并用IBOutlet关联到代码里(比如叫tableViewHeightConstraint),然后做这些配置:
class ViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! @IBOutlet weak var tableView: UITableView! @IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint! // 存储每个TextView的内容,作为TableView的数据源 var textItems: [String] = [] override func viewDidLoad() { super.viewDidLoad() tableView.delegate = self tableView.dataSource = self tableView.register(TextViewCell.self, forCellReuseIdentifier: "TextViewCell") // 关闭TableView滚动 tableView.isScrollEnabled = false // 开启自动行高计算 tableView.rowHeight = UITableView.automaticDimension // 必须设置预估行高,否则automaticDimension不生效 tableView.estimatedRowHeight = 100 } // 点击添加按钮的方法 @IBAction func addTextViewButtonTapped(_ sender: UIButton) { textItems.append("") tableView.reloadData() // 更新TableView的高度 updateTableViewHeight() } // 更新TableView高度的工具方法 private func updateTableViewHeight() { // 必须等TableView完成内容更新后再计算高度,所以用async DispatchQueue.main.async { self.tableViewHeightConstraint.constant = self.tableView.contentSize.height // 强制更新布局,让变化生效 self.view.layoutIfNeeded() } } }
3. 绑定数据源并处理高度联动
在TableView的代理方法里,把回调绑定上,这样当用户输入文字时,会触发单元格高度更新,进而更新TableView和外层ScrollView:
extension ViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return textItems.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "TextViewCell", for: indexPath) as! TextViewCell cell.textView.text = textItems[indexPath.row] // 绑定高度变化的回调 cell.onHeightChanged = { [weak self] in guard let self = self else { return } // 先更新数据源 self.textItems[indexPath.row] = cell.textView.text // 让TableView重新计算当前行的高度(用begin/endUpdates比reloadRows更高效) tableView.beginUpdates() tableView.endUpdates() // 更新TableView的整体高度 self.updateTableViewHeight() } return cell } }
4. 外层ScrollView的布局关键
最后要确保外层UIScrollView的自动布局是正确的:
- 给ScrollView设置
top、leading、trailing、bottom约束到父视图的边缘 - 给TableView设置
top、leading、trailing、bottom约束到ScrollView的边缘 - 给TableView加一个
width约束,等于ScrollView的宽度(避免出现横向滚动)
这样ScrollView会自动根据子视图的高度计算contentSize,不需要手动设置。
几个注意点
- 一定要给TableView设置
estimatedRowHeight,否则UITableView.automaticDimension不会生效 - 单元格里的
UITextView必须和contentView有完整的约束(上下左右都绑定),不能有布局歧义 - 在
textViewDidChange里调用beginUpdates()和endUpdates(),能让TableView只更新当前行的高度,性能更好
这样一套配置下来,点击添加按钮会新增带TextView的单元格,输入文字时TextView自动增高,单元格高度跟着调整,TableView的高度也会自动适配,外层ScrollView的内容尺寸也会同步更新,完全满足你的需求!
内容的提问来源于stack exchange,提问作者Preetika
相关产品推荐
相关产品推荐

