如何实现自定义UITableViewCell中progressBar的勾选触发动画效果?
Hey Matt, let's break down what's going wrong with both of your approaches and fix that progress bar animation!
First Approach: Why You Can't Access the Cell
The immediate issue here is that you've mixed up the section and row parameters when creating your IndexPath.
IndexPath(item:row, section:section) is the correct initializer, but you wrote:
IndexPath(item: indexSection!, section: indexRow!)
This points to a non-existent cell position, so tableView.cellForRow(at:) returns nil—hence the fatal error when you force-unwrap cell.
Even if you fixed this index mix-up, handling cell animations in the table view controller's delegate is not ideal. Cell-specific interactions should stay encapsulated within the cell itself to keep your code clean and avoid tight coupling.
Second Approach: Why the Animation Isn't Working
You're conflicting two animation systems here:
setProgress(_:animated:)triggers theUIProgressView's built-in animation (with a fixed short duration)- Wrapping this in
UIView.animatetries to override that timing, which breaks the smooth transition and causes the sudden jump.
The fix is to skip the animated parameter entirely and directly modify the progress property inside a UIView.animate block. This gives you full control over the duration.
Corrected Code for TaskCell.swift
Here's how to adjust your checkbox action handler:
@IBAction func checkBoxAction(_ sender: Any) { // Toggle the checked state in your data model first let newCheckedState = !items![indexRow!].checked items![indexRow!].checked = newCheckedState // Notify the delegate of the state change delegate?.changeButton( state: newCheckedState, indexSection: indexSection!, indexRow: indexRow!, itemID: itemID ) // Animate the progress bar with your desired duration let targetProgress = newCheckedState ? 1.0 : 0.0 let animationDuration = newCheckedState ? 5.0 : 1.0 UIView.animate(withDuration: animationDuration) { self.progressBar.progress = targetProgress } }
Bonus: Improve Code Robustness
Storing items, indexSection, and indexRow directly in the cell is risky—these values can become invalid when cells are reused or the table view refreshes. A better pattern is to pass a task model directly to the cell:
// Inside TaskCell.swift class TaskCell: UITableViewCell { var taskItem: TaskItem! { didSet { // Configure initial state when the model is set progressBar.progress = taskItem.checked ? 1.0 : 0.0 // Update your checkbox state here too } } @IBAction func checkBoxAction(_ sender: Any) { taskItem.checked = !taskItem.checked delegate?.changeButton(state: taskItem.checked, itemID: taskItem.id) let targetProgress = taskItem.checked ? 1.0 : 0.0 let animationDuration = taskItem.checked ? 5.0 : 1.0 UIView.animate(withDuration: animationDuration) { self.progressBar.progress = targetProgress } } }
Then in your table view controller's cellForRowAt method, just set cell.taskItem = yourTaskModel—this eliminates forced unwraps and makes your code more maintainable.
内容的提问来源于stack exchange,提问作者M. Tol

