如何为Section显示添加自定义动画?sectionsHaveBeenAdded回调是否适用?
嘿,我来帮你梳理下这个问题~首先直接回答你的疑问:sectionsHaveBeenAdded 这个回调并不完全适用于你当前的场景,接下来我会详细解释原因,再给出更合适的实现思路。
为什么sectionsHaveBeenAdded不适用?
这个回调的触发时机是当全新的Section被添加到列表数据源中的时候。但你现在的逻辑是通过修改section.hidden属性,再调用section.evaluateHidden()来控制已有Section的显示/隐藏——这种操作本质上是让列表重新计算可见Section,而不是“添加”一个新的Section到数据源里。所以当你把一个原本隐藏的Section设置为显示时,这个回调并不会被触发,它覆盖不了你需要的“隐藏后再显示”的场景。
正确的实现思路
针对你通过hidden属性控制Section显示隐藏的场景,推荐两种可行的方案:
方案一:监听Section的隐藏状态变化,触发自定义动画
如果你的Section类支持添加状态监听(很多自定义表单/列表框架都会提供这类扩展),可以给Section绑定一个状态变化的闭包,在它从隐藏变为可见时手动执行动画。示例代码如下:
// 假设你的Section实例为targetSection targetSection.didChangeHiddenState = { [weak self] isHidden in guard let self = self, !isHidden else { return } // 获取该Section在列表中的索引 guard let sectionIndex = self.form.index(of: targetSection) else { return } let sectionIndexSet = IndexSet(integer: sectionIndex) // 先让列表完成Section的显示刷新,再叠加自定义动画 self.tableView.performBatchUpdates({ self.tableView.reloadSections(sectionIndexSet, with: .none) }, completion: { _ in // 对Section的Header做淡入动画 if let headerView = self.tableView.headerView(forSection: sectionIndex) { headerView.alpha = 0 UIView.animate(withDuration: 0.3) { headerView.alpha = 1 } } // 对Section内的每行Cell做滑入动画(带延迟,更有层次感) for (rowIndex, row) in targetSection.rows.enumerated() { guard let indexPath = self.form.indexPath(for: row) else { continue } let cell = self.tableView.cellForRow(at: indexPath) cell?.transform = CGAffineTransform(translationX: self.view.bounds.width, y: 0) UIView.animate(withDuration: 0.3, delay: 0.1 * Double(rowIndex), options: .curveEaseOut) { cell?.transform = .identity } } }) }
方案二:在调用evaluateHidden()后手动触发动画
如果你的框架不支持状态监听,可以在设置section.hidden = false并调用evaluateHidden()之后,主动获取Section的索引并执行动画:
// 你的原有逻辑 if condition == true { targetSection.hidden = false } else { targetSection.hidden = true } targetSection.evaluateHidden() // 仅当Section变为可见时执行动画 if !targetSection.hidden { DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in guard let self = self, let sectionIndex = self.form.index(of: targetSection) else { return } // 这里执行和方案一类似的自定义动画逻辑 let headerView = self.tableView.headerView(forSection: sectionIndex) // ... 动画代码省略 } }
这里加了asyncAfter是为了等待列表完成刷新布局,确保能正确获取到Section的视图。
补充:如果是新增Section到数据源
如果你的场景中确实存在“新增Section到列表”的情况,那sectionsHaveBeenAdded就可以派上用场了——在这个回调里,你可以拿到新增的Section和它们的索引,直接执行插入动画:
override func sectionsHaveBeenAdded(_ sections: [Section], at indexes: IndexSet) { super.sectionsHaveBeenAdded(sections, at: indexes) // 对新增的Section执行自定义动画 for sectionIndex in indexes { let headerView = self.tableView.headerView(forSection: sectionIndex) headerView?.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) UIView.animate(withDuration: 0.3) { headerView?.transform = .identity } } }
注意事项
- 一定要用
weak self避免循环引用,防止内存泄漏。 - 动画要和列表的刷新逻辑配合,避免在刷新过程中重复触发动画,导致布局混乱。
- 如果你的列表框架有自己的刷新动画机制,尽量和它的生命周期对齐,不要强行覆盖原有逻辑。
内容的提问来源于stack exchange,提问作者hopy

