0

I have already read some issues and not properly uses about spring animations in Swift but I am a little bit confused about this case. I have a ViewController which has a UITableView. I would like to add some little spring bouncing animation to its cells. When a cell tapped it should be expanding and running the bouncing animation and it works perfectly for the first time. But after a cell is expanded and tapped again, the animation is ignored, but the code inside animations is perfectly running (e.g. a print command). Do you have any idea to achieve that goal to make the animation work twice or more? I think I theoretically missed something.

My ViewController:

class TestTableViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    var selectedIndex: IndexPath = IndexPath(row: 0, section: 0)

    var isExpanded = [Bool]()

    var currentExpandedIndexPath: IndexPath?


    override func viewDidLoad() {
        super.viewDidLoad()

        isExpanded = Array(repeating: false, count: 15)

        tableView.delegate = self
        tableView.dataSource = self

    }
}

extension TestTableViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 15
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TestTableViewCell", for: indexPath) as! TestTableViewCell

        cell.selectionStyle = .none
        cell.animate(duration: 0.5, delay: 0.2, damping: 0.5, options: .curveEaseOut)

        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        if isExpanded[indexPath.row] == true { return 300 }
        return 150
    }

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {


        // This is just for handling that to be only one cell that is expanded at one time
        isExpanded[indexPath.row] = !isExpanded[indexPath.row]
        if (currentExpandedIndexPath != nil) {
            if (indexPath == currentExpandedIndexPath) {
                currentExpandedIndexPath = nil
            } else {
                isExpanded[currentExpandedIndexPath!.row] = false
                currentExpandedIndexPath = indexPath
            }
        } else {
            currentExpandedIndexPath = indexPath
        }

        tableView.beginUpdates()
        tableView.reloadRows(at: [indexPath], with: .automatic)
        tableView.endUpdates()
    }
}

And this is my TableViewCell class:

class TestTableViewCell: UITableViewCell {

    func animate(duration: TimeInterval, delay: TimeInterval, damping: CGFloat, options: UIView.AnimationOptions = []) {

        UIView.animate(withDuration: duration, delay: delay, usingSpringWithDamping: damping, initialSpringVelocity: 1,  options: options, animations: {
            self.contentView.layoutIfNeeded()
            print("this command runs everytime")
        })
    }

}

These links are GIFs that show how it's working now. If I tap another cell after one expanded it has correct animation (first link). But if I tap the expanded one, it is not animated (second link).

Tapping one after one expanded

Tapping the same cell after it is expanded

1 Answers1

1

There are two possibilities here:

1 - Following code possibly overrides your animation:

tableView.reloadRows(at: [indexPath], with: .automatic)

Try passing .none or some other flag.

2 - Following code must be called from main thread a.k.a DispatchQueue.main.

self.contentView.layoutIfNeeded()

There is also a 3rd possibility that is reloadRows does not necessarily call cellForRowAt where you are currently handling animations.

Nirav Bhatt
  • 6,940
  • 5
  • 45
  • 89
  • Unfortunately the first and second possibility are not working. The third one I think is not a possibility because the print command is running inside the `animate` method – Ágoston Szabó Jan 30 '20 at 12:06