You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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
}

目前滑动操作期间,箭头并未从向上平滑旋转至向右,恳请帮忙排查问题或提供更优实现方案。


问题根源分析

  1. 静态快照无法承载动画:你将UIImageView的快照设置为moreAction.image,这只是一张静态图片,UIContextualAction的image属性本身不支持动态动画,后续无法对其做旋转操作。
  2. 旋转方法未绑定滑动进度:rotateIcon方法仅定义但未被调用,也没有和UITableView的滑动进度关联,自然不会触发动态旋转。
  3. 视图层级定位错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 18:24:58