自定义UITableViewCell中layoutSubviews()对部分复用单元格不生效
看起来你遇到的问题是自定义编辑动画在cell滚动到底部时失效——虽然layoutSubviews里打印的frame值正确,但界面上containerView的位置就是不对。我来帮你拆解下可能的原因和对应的解决方案:
1. 调整layoutSubviews中super.layoutSubviews()的调用顺序
你当前的代码是先设置子视图的frame,再调用super.layoutSubviews(),但UITableViewCell的父类在layoutSubviews中会重新调整contentView及其子视图的布局,这会直接覆盖你刚才手动设置的frame值。正确的做法是先让系统完成基础布局,再设置自己的子视图位置:
override func layoutSubviews() { super.layoutSubviews() // 先调用super,让系统处理默认布局逻辑 self.deleteButtonView.frame.origin.x = self.viewPosition - 80 self.disclosureButtonView.frame.origin.x = self.contentView.bounds.width - (self.viewPosition / 2) self.containerView.frame.origin.x = self.viewPosition self.alarmImageNameView.alpha = self.viewAlpha self.alarmImageDateView.alpha = self.viewAlpha self.alarmSwitchView.alpha = self.viewAlpha self.deleteButton.transform = self.buttonTransform }
2. 修复Cell复用状态不一致的问题
当tableView滚动到底部时,会复用屏幕外的旧cell。如果这些旧cell之前处于编辑/非编辑状态,而你没有在cellForRowAt中同步当前tableView的编辑状态,就会导致viewPosition、viewAlpha这些变量的值和实际状态不匹配,进而引发布局异常。你需要在复用cell时,强制同步当前状态:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let alarmProperties = self.alarms[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TableViewCell cell.delegate = self cell.selectionStyle = .none cell.alarmTimeLabel.text = "\(alarmProperties.hour):\(alarmProperties.minute)" cell.alarmSwitch.isOn = alarmProperties.isActive // 复用cell时,根据当前tableView的编辑状态重置cell的状态变量 let isEditing = tableView.isEditing cell.viewPosition = isEditing ? 80 : 0 cell.viewAlpha = isEditing ? 0 : 1 cell.buttonTransform = isEditing ? CGAffineTransform(rotationAngle: 90 * (.pi / 180)) : .identity cell.setNeedsLayout() // 触发重新布局 return cell }
另外,建议在cell的prepareForReuse方法中重置这些状态变量,彻底避免复用旧状态的影响:
class TableViewCell: UITableViewCell { // ... 你的其他代码 override func prepareForReuse() { super.prepareForReuse() self.viewPosition = 0 self.viewAlpha = 1 self.buttonTransform = .identity self.setNeedsLayout() } }
3. 额外建议:优先用AutoLayout替代直接操作frame
直接修改frame在复杂布局和cell复用场景下很容易出现各种奇怪的问题,推荐用AutoLayout来定义子视图的约束——比如给containerView添加一个leading约束,编辑时修改约束的constant为80,非编辑时设为0,然后在动画块中调用layoutIfNeeded()。这种方式由系统自动处理布局更新,稳定性会好很多。
按上面的步骤修改后,应该就能解决滚动到底部时布局失效的问题了。
内容的提问来源于stack exchange,提问作者Josef Zoller

