UITableView reloadData后UIView旋转动画失效问题求助
这个问题我之前碰到过类似的,核心是reloadData()会重置tableView头部视图的状态,再加上你原来的Core Animation动画方式存在状态持久化的问题,才导致动画在reload后失效。下面给你几个针对性的解决方案:
1. 替换Core Animation为UIKit动画,持久化视图状态
你原来用CABasicAnimation实现旋转,虽然设置了isRemovedOnCompletion = false和fillMode = kCAFillModeForwards,但这只是让layer显示动画后的状态,并没有真正修改视图的transform属性。当tableView reload时,视图的layer会被重置,之前的动画状态就丢失了。
改成UIKit的动画,它会在动画结束后自动更新视图的transform属性,状态是持久化的:
extension UIView { func rotate(_ toValue: CGFloat, duration: CFTimeInterval = 0.2) { UIView.animate(withDuration: duration) { self.transform = CGAffineTransform(rotationAngle: toValue) } } }
2. 不要存储失效的Header实例,直接从TableView获取当前视图
你在viewForHeaderInSection里把每个dequeue出来的header都加到sectionHeaders数组里,但tableView的header是会被复用的,数组里的实例可能已经不是当前显示在界面上的那个了。调用旧实例的setExpanded自然看不到效果。
直接在需要操作的时候从tableView获取当前的header:
@objc func handleExpandClose(sender: UITapGestureRecognizer) { guard let section = sender.view?.tag else { return } // ... 其他代码 ... let isExpanded = menuItems[section].isExpanded menuItems[section].isExpanded = !isExpanded // 替换原来的sectionHeaders[section].setExpanded if let header = tableView.headerView(forSection: section) as? TableSectionHeader { header.setExpanded(expanded: !isExpanded) } // ... 插入/删除行的代码 ... } private func closeOpenedSections(section: Int) { var closeIndexPaths = [IndexPath]() for (sectionIndex, sec) in menuItems.enumerated() { guard let subItems = sec.subItems, sec.isExpanded, sectionIndex != section else { continue } sec.isExpanded = false // ... 收集indexPath的代码 ... // 替换原来的sectionHeaders[sectionIndex].setExpanded if let header = tableView.headerView(forSection: sectionIndex) as? TableSectionHeader { header.setExpanded(expanded: false) } tableView.deleteRows(at: closeIndexPaths, with: .fade) } }
之后就可以删掉sectionHeaders数组了,避免维护失效的实例。
3. Reload时同步Header的初始状态
reloadData()会重新创建或复用header,所以要在viewForHeaderInSection里根据数据的isExpanded状态,设置header的初始旋转状态,确保reload后视图状态和数据一致:
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { if let sectionHeader = self.tableView.dequeueReusableHeaderFooterView(withIdentifier: "TableSectionHeader") as? TableSectionHeader { sectionHeader.setUpHeader(emphasize: menuItems[section].emphasize) sectionHeader.titleLabel.text = menuItems[section].title let tap = UITapGestureRecognizer(target: self, action: #selector(MenuTableViewController.handleExpandClose(sender:))) sectionHeader.addGestureRecognizer(tap) sectionHeader.indicatorImage.isHidden = menuItems[section].subItems != nil ? false : true sectionHeader.tag = section // 新增:根据当前展开状态设置初始旋转 let isExpanded = menuItems[section].isExpanded sectionHeader.setExpanded(expanded: isExpanded) return sectionHeader } return UIView() }
把这三个步骤结合起来,就能解决reloadData后动画失效的问题了。
内容的提问来源于stack exchange,提问作者Brudus

