如何在UITableView的UISwipeActionsConfiguration中实现UIImageView旋转动画?
UITableView滑动操作图标旋转动画失效问题
我想给UITableView单元格的滑动操作添加旋转图标动画,目标是让箭头图标初始向上,在滑动过程中平滑旋转至向右。尝试通过UIContextualAction关联UIImageView实现旋转,但动画未达到预期效果。
简化后的代码如下:
class CustomTableViewCell: UITableViewCell { func rotateIcon(angle: CGFloat) { for subview in contentView.subviews { if let imageView = subview as? UIImageView { UIView.animate(withDuration: 0.25) { imageView.transform = CGAffineTransform(rotationAngle: angle) } } } } } override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let moreAction = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in print("More option selected for item: \(self.items[indexPath.row])") completion(true) } moreAction.backgroundColor = UIColor.blue let imageView = UIImageView(image: UIImage(systemName: "arrow.up.circle")) imageView.contentMode = .scaleAspectFit imageView.transform = CGAffineTransform(rotationAngle: 0) moreAction.image = UIGraphicsImageRenderer(size: imageView.bounds.size).image { _ in imageView.drawHierarchy(in: imageView.bounds, afterScreenUpdates: true) } let swipeConfiguration = UISwipeActionsConfiguration(actions: [moreAction]) return swipeConfiguration }
目前滑动操作期间,箭头并未从向上平滑旋转至向右,恳请帮忙排查问题或提供更优实现方案。
问题根源分析
- 静态快照无法承载动画:你将
UIImageView的快照设置为moreAction.image,这只是一张静态图片,UIContextualAction的image属性本身不支持动态动画,后续无法对其做旋转操作。 - 旋转方法未绑定滑动进度:
rotateIcon方法仅定义但未被调用,也没有和UITableView的滑动进度关联,自然不会触发动态旋转。 - 视图层级定位错误:
rotateIcon遍历的是cell的contentView子视图,但滑动操作的图标属于UIContextualAction的视图层级,不在cell的contentView内,该方法无法找到目标ImageView。
可行解决方案
方案一:自定义Action视图+监听滑动进度(实现滑动过程中动态旋转)
通过自定义视图承载图标,结合滚动代理监听滑动进度,实时更新旋转角度:
class CustomSwipeActionView: UIView { private let arrowImageView = UIImageView(image: UIImage(systemName: "arrow.up.circle")) override init(frame: CGRect) { super.init(frame: frame) setupSubviews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupSubviews() { arrowImageView.contentMode = .scaleAspectFit arrowImageView.tintColor = .white addSubview(arrowImageView) arrowImageView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ arrowImageView.centerXAnchor.constraint(equalTo: centerXAnchor), arrowImageView.centerYAnchor.constraint(equalTo: centerYAnchor), arrowImageView.widthAnchor.constraint(equalToConstant: 24), arrowImageView.heightAnchor.constraint(equalToConstant: 24) ]) } // 根据滑动进度更新旋转角度(0对应向上,1对应向右) func updateRotation(with progress: CGFloat) { let targetAngle = progress * .pi / 2 arrowImageView.transform = CGAffineTransform(rotationAngle: targetAngle) } } // 在ViewController中实现相关逻辑 var currentSwipeView: CustomSwipeActionView? var currentIndexPath: IndexPath? override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let moreAction = UIContextualAction(style: .normal, title: "More") { [weak self] (_, _, completion) in print("More option selected for item: \(self?.items[indexPath.row] ?? "")") completion(true) } moreAction.backgroundColor = .blue // 创建自定义视图并绑定到Action let customView = CustomSwipeActionView(frame: CGRect(x: 0, y: 0, width: 60, height: tableView.rowHeight)) moreAction.addSubview(customView) customView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ customView.leadingAnchor.constraint(equalTo: moreAction.leadingAnchor), customView.trailingAnchor.constraint(equalTo: moreAction.trailingAnchor), customView.topAnchor.constraint(equalTo: moreAction.topAnchor), customView.bottomAnchor.constraint(equalTo: moreAction.bottomAnchor) ]) currentSwipeView = customView currentIndexPath = indexPath return UISwipeActionsConfiguration(actions: [moreAction]) } // 监听滑动进度,更新旋转角度 override func scrollViewDidScroll(_ scrollView: UIScrollView) { guard let tableView = scrollView as? UITableView, let indexPath = currentIndexPath, let swipeView = currentSwipeView, let cell = tableView.cellForRow(at: indexPath) else { return } let swipeOffset = cell.frame.origin.x - tableView.contentOffset.x // 计算滑动进度:0(未滑动)到1(完全展开),60为滑动操作宽度 let progress = max(0, min(1, -swipeOffset / 60)) swipeView.updateRotation(with: progress) } // 滑动结束后重置状态 override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { if !decelerate { resetSwipeState() } } override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { resetSwipeState() } private func resetSwipeState() { currentSwipeView = nil currentIndexPath = nil }
方案二:点击Action时触发旋转动画(仅点击时生效)
若仅需要点击操作按钮时旋转图标,可简化为以下实现:
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { let moreAction = UIContextualAction(style: .normal, title: "More") { (action, view, completion) in // 找到Action视图内的ImageView并执行旋转动画 if let imageView = view.subviews.compactMap({ $0 as? UIImageView }).first { UIView.animate(withDuration: 0.25) { imageView.transform = CGAffineTransform(rotationAngle: .pi / 2) } completion: { _ in completion(true) // 可选:动画结束后重置图标方向 UIView.animate(withDuration: 0.25) { imageView.transform = .identity } } } else { completion(true) } } moreAction.backgroundColor = .blue let imageView = UIImageView(image: UIImage(systemName: "arrow.up.circle")) imageView.contentMode = .scaleAspectFit imageView.tintColor = .white imageView.frame = CGRect(x: 0, y: 0, width: 24, height: 24) moreAction.addSubview(imageView) imageView.center = moreAction.center return UISwipeActionsConfiguration(actions: [moreAction]) }
内容的提问来源于stack exchange,提问作者NinjaDeveloper
相关产品推荐
相关产品推荐

