// resizing table view cells on data change import UIKit struct MyDataStruct { var title: String = "" var tapCount: Int = 0 } class SimpleCell: UITableViewCell { var callback: ((Int, UITableViewCell)->())? let theLabel: UILabel = { let v = UILabel() v.numberOfLines = 0 v.backgroundColor = .yellow return v }() let btn: UIButton = { let v = UIButton() v.backgroundColor = .systemRed return v }() private var title: String = "" private var tapCount: Int = 0 override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) commonInit() } required init?(coder: NSCoder) { super.init(coder: coder) commonInit() } func setData(_ d: MyDataStruct) -> Void { title = d.title tapCount = d.tapCount theLabel.text = title + " Taps: \(tapCount)" } func commonInit() -> Void { [theLabel, btn].forEach { v in v.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(v) } let g = contentView.layoutMarginsGuide NSLayoutConstraint.activate([ theLabel.topAnchor.constraint(equalTo: g.topAnchor), theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor), theLabel.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor), btn.topAnchor.constraint(equalTo: g.topAnchor), btn.trailingAnchor.constraint(equalTo: g.trailingAnchor), btn.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor), btn.widthAnchor.constraint(equalToConstant: 100.0), btn.leadingAnchor.constraint(equalTo: theLabel.trailingAnchor, constant: 8.0), ]) btn.setTitle("Tap Me", for: []) btn.addTarget(self, action: #selector(gotTap(_:)), for: .touchUpInside) } @objc func gotTap(_ sender: Any) { tapCount += 1 theLabel.text = title + " Taps: \(tapCount)" callback?(tapCount, self) } } class RowSizingTableViewController: UITableViewController { var myData: [MyDataStruct] = [] override func viewDidLoad() { super.viewDidLoad() for i in 0..<20 { let t = MyDataStruct(title: "Row: \(i)", tapCount: 0) myData.append(t) } tableView.register(SimpleCell.self, forCellReuseIdentifier: "cell") } // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return myData.count } override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { print(#function, indexPath, myData[indexPath.row]) return CGFloat(60 + (myData[indexPath.row].tapCount * 20)) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SimpleCell cell.setData(myData[indexPath.row]) cell.callback = { [weak self] n, c in guard let self = self, let pth = self.tableView.indexPath(for: c) else { return } // update data self.myData[pth.row].tapCount = n // both // beginUpdates() / endUpdates() // and // performBatchUpdates() // seem to work fine... self.tableView.beginUpdates() self.tableView.endUpdates() //self.tableView.performBatchUpdates(nil, completion: nil) } return cell } }