UITableViewCell点击后高度不生效需滚动才正常,如何实现点击立即展开并带动画
解决方案
问题根源
你只在cell内部修改了约束,UITableView没有感知到cell高度的变化,只有当cell滚动出可视区域再复用时,tableView才会重新计算cell高度,因此会出现滚动后才生效的问题。
改造步骤
1. 配置TableView自动高度
首先确保你的UITableView开启了自动高度计算,在持有tableView的ViewController的viewDidLoad中添加如下配置:
// 开启自动高度 tableView.rowHeight = UITableView.automaticDimension // 设一个和收起状态匹配的预估高度,减少计算误差(你当前收起状态cell高度为80+8*2=96) tableView.estimatedRowHeight = 96
2. 给自定义Cell添加状态回调
在你的自定义UITableViewCell类中新增回调属性,用于点击后通知外部刷新tableView:
// 点击展开的回调闭包 var onStateChanged: (() -> Void)?
3. 改造Cell的点击方法
修改expand()方法,切换约束后触发回调:
@objc func expand() { // 切换约束状态 self.labelCompactHeightConstraint.isActive = !self.labelCompactHeightConstraint.isActive self.labelExpandedHeightConstraint.isActive = !self.labelExpandedHeightConstraint.isActive // 通知外部处理刷新逻辑 self.onStateChanged?() }
4. 在ViewController中处理刷新逻辑
首先你需要新增一个存储属性,记录每个行的展开状态,避免cell复用导致状态错乱:
// 存储所有已展开的行位置 private var expandedIndexPaths = Set<IndexPath>()
然后在tableView(_:cellForRowAt:)方法中配置回调,处理状态切换和刷新逻辑:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "你的Cell复用ID", for: indexPath) as! 你的自定义Cell类 // 其他cell配置逻辑 // 先根据当前存储的状态设置cell的约束,避免复用错乱 if expandedIndexPaths.contains(indexPath) { cell.labelCompactHeightConstraint.isActive = false cell.labelExpandedHeightConstraint.isActive = true } else { cell.labelExpandedHeightConstraint.isActive = false cell.labelCompactHeightConstraint.isActive = true } // 配置状态切换回调 cell.onStateChanged = { [weak self] in guard let self = self else { return } // 切换当前行的展开状态 if self.expandedIndexPaths.contains(indexPath) { self.expandedIndexPaths.remove(indexPath) } else { self.expandedIndexPaths.insert(indexPath) } // 触发tableView高度更新,自带过渡动画 tableView.beginUpdates() tableView.endUpdates() // 附加约束变化的动画,让展开过程更流畅 UIView.animate(withDuration: 0.3) { cell.layoutIfNeeded() } } return cell }
如果你不需要保留切换收起的能力,只需要单次展开,删除状态切换的逻辑即可,回调中只需要触发tableView更新即可。
内容的提问来源于stack exchange,提问作者user2334064
相关产品推荐
相关产品推荐

