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

TableView Cell内UIView动画不一致问题的修复咨询

iOS TableView Cell 动画异常修复方案

问题现象

  • 带背景色动画的TableView Cell滑出屏幕再返回后,动画速度异常加快;
  • 点击该Cell打开新ViewController,返回原页面后动画完全停止;
  • 已尝试在cellForRowAt中启动动画,未解决问题。

相关代码

TableView代理方法

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if let cell = cell as? DirectionSummaryTableViewCell {
        if let isLive = cell.isLive {
            if isLive {
                cell.animateBusStatusBackground()
            } else {
                cell.removeAnimation()
            }
        }
    }
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let cell = tableView.dequeueReusableCell(withIdentifier: DirectionSummaryTableViewCell.identifier, for: indexPath) as?  DirectionSummaryTableViewCell else { return UITableViewCell() }

    cell.configure(with: viewModel.placeToPlacePossibleDirections[indexPath.section][indexPath.row].directions, 
tripDuration: viewModel.placeToPlacePossibleDirections[indexPath.section][indexPath.row].tripTime,
destinationArrivalTime:viewModel.placeToPlacePossibleDirections[indexPath.section][indexPath.row].reachBy,
busDepartureTime: viewModel.placeToPlacePossibleDirections[indexPath.section][indexPath.row].directions.routes[0].departureTime, 
startLocation: viewModel.placeToPlacePossibleDirections[indexPath.section][indexPath.row].directions.routes[0].stops[0].name, addFullLabel: true, 
isLive: viewModel.placeToPlacePossibleDirections[indexPath.section][indexPath.row].responseType == "realtime")

    return cell
}

Cell动画与配置方法

func animateBusStatusBackground() {
    UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse, .curveEaseInOut], animations: { [weak self] in
        if self?.busStatusView.backgroundColor == .red {
            self?.busStatusView.backgroundColor = .grey6
        } else {
            self?.busStatusView.backgroundColor = .red
        }
    }, completion: nil)
}

func removeAnimation() {
    self.busStatusView.layer.removeAllAnimations()
    self.layer.removeAllAnimations()
    self.layoutIfNeeded()
}

func configure(with directions: PlaceToPlaceBusDirections, tripDuration: Double, destinationArrivalTime: String, busDepartureTime: String, startLocation: String, addFullLabel: Bool, isLive: Bool) {
    self.directions = directions
    self.isLive = isLive
    self.collectionView.reloadData()
    self.collectionView.layoutIfNeeded()
    var formattedTripDuration = ""
    if tripDuration > 60 {
        let hrs = Int(tripDuration / 60)
        formattedTripDuration += String(hrs) + " hr"
        if hrs > 1 { formattedTripDuration += "s " } else { formattedTripDuration += " " }
    }
    formattedTripDuration += String(Int(tripDuration) % 60)

    self.tripDurationLabel.text = formattedTripDuration + " mins"
    self.destinationArrivalTimeLabel.text = Date.dateStringFromString(dateString: destinationArrivalTime)
    if addFullLabel {
        self.busDeparturePlaceAndTimeLabel.text = ("Leaves at " + Date.dateStringFromString(dateString: busDepartureTime) + " from " + startLocation).maxLength(length: 40)
    } else {
        self.busDeparturePlaceAndTimeLabel.text = ("Leaves at " + Date.dateStringFromString(dateString: busDepartureTime)).maxLength(length: 40)
    }

    if !isLive {
        busStatusText.text = "Live"
        busStatusText.textColor = .white
    } else {
        busStatusText.text = "Scheduled"
        busStatusText.textColor = .grey2
        busStatusView.backgroundColor = .grey6
    }

    removeAnimation()
}

问题根源

  1. 动画叠加:Cell复用时未完全清除旧动画,重复启动导致多动画叠加,速度异常;
  2. 动画销毁:ViewController进入后台时,UIView动画被系统暂停/销毁,返回后未重新触发;
  3. 逻辑冲突:configure方法无条件调用removeAnimation(),会清空willDisplay中刚启动的动画。

修复步骤

1. 重构动画方法,避免叠加

修改animateBusStatusBackground,启动前强制清除旧动画,并固定动画起点:

func animateBusStatusBackground() {
    // 清除已有动画,避免叠加
    busStatusView.layer.removeAllAnimations()
    
    // 固定初始背景色,确保动画状态一致
    busStatusView.backgroundColor = .red
    
    UIView.animate(withDuration: 1.0, delay: 0.0, options: [.repeat, .autoreverse, .curveEaseInOut, .allowUserInteraction], animations: { [weak self] in
        self?.busStatusView.backgroundColor = .grey6
    }, completion: nil)
}

2. 修正configure的动画清除逻辑

仅在非Live状态下清除动画,避免误删需要运行的动画:

func configure(with directions: PlaceToPlaceBusDirections, tripDuration: Double, destinationArrivalTime: String, busDepartureTime: String, startLocation: String, addFullLabel: Bool, isLive: Bool) {
    // 省略其他配置逻辑...
    
    if !isLive {
        busStatusText.text = "Live"
        busStatusText.textColor = .white
        busStatusView.backgroundColor = .red
        removeAnimation() // 仅非Live状态清除动画
    } else {
        busStatusText.text = "Scheduled"
        busStatusText.textColor = .grey2
        busStatusView.backgroundColor = .grey6
        // Live状态下不调用removeAnimation
    }
}

3. 页面返回时重新触发动画

在TableView所在的ViewController中,重写viewWillAppear,手动刷新可见Cell的动画:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    tableView.visibleCells.forEach { cell in
        guard let customCell = cell as? DirectionSummaryTableViewCell else { return }
        if customCell.isLive == true {
            customCell.animateBusStatusBackground()
        } else {
            customCell.removeAnimation()
        }
    }
}

4. 可选:改用Core Animation实现(更稳定)

如果UIView动画仍有问题,替换为Core Animation,避免后台切换导致的动画失效:

func animateBusStatusBackground() {
    busStatusView.layer.removeAnimation(forKey: "backgroundColorAnimation")
    
    let colorAnimation = CABasicAnimation(keyPath: "backgroundColor")
    colorAnimation.fromValue = UIColor.red.cgColor
    colorAnimation.toValue = UIColor.grey6.cgColor
    colorAnimation.duration = 1.0
    colorAnimation.repeatCount = .infinity
    colorAnimation.autoreverses = true
    colorAnimation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
    
    busStatusView.layer.add(colorAnimation, forKey: "backgroundColorAnimation")
    busStatusView.backgroundColor = .grey6 // 固定最终状态
}

func removeAnimation() {
    busStatusView.layer.removeAnimation(forKey: "backgroundColorAnimation")
    busStatusView.backgroundColor = isLive ? .grey6 : .red
}

验证效果

  • 滑动Cell出屏幕再返回,动画速度保持稳定;
  • 跳转页面返回后,动画自动恢复运行;
  • Cell复用后动画状态无异常叠加。

内容的提问来源于stack exchange,提问作者Sahil Sahu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 15:21:02