Kex
Kex

Reputation: 8579

Animating tableview height constraint but animation is jumping

I want to animate the height constraint of a tableview I have. I have set up a small test project. The table view sits in a container view.

enter image description here

My code is like so:

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var heightConstraint: NSLayoutConstraint!
    @IBOutlet weak var contentView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
    }

    @IBAction func didTapAnimate(_ sender: Any) {
        heightConstraint.constant = 400
        UIView.animate(withDuration: 1.0, animations: {() in
            self.contentView.layoutIfNeeded()
        })
    }

}

extension ViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

}

extension ViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.backgroundColor = UIColor.red
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }

}

The problem is, when I hit the animate button the animation doesn't happen smoothly. The table first jumps up a certain height and then animates the rest. Example here:

enter image description here

What am I doing wrong here? Why does the table jump? I need the animation to be smooth. Any tips or pointers on this would be greatly appreciated! Thanks!

Upvotes: 1

Views: 1746

Answers (3)

agibson007
agibson007

Reputation: 4373

So your issues are not with the animation but with the reload of the UITableView. A tableview only holds the visible cells plus it loads an additional cell about equal to the cell height on the top and the bottom. But your height grows so much that it has to reload to keep visible cells making the reload and table look jumpy. So how I would approach this is by first adding an additional constraint from the top of the tableview to the bottom safe area inset that inversely equal to your height constraint. You will see why in a minute Here is what it would look like. Sorry I left off the content view but it should all work the same..

topConstraint Here is an image of the entire setup. completesetup

Now to the code and the trick. The trick is when making the tableview bigger you do that first without animation but don't move it up. Then you animate the top constraint we just added to be equal to the -height. That way the user does not see the reload but it looks like it grows upward. To make it smaller you move it down then change the size so the user never sees the reload. Here is the code and I added comments to help you understand what I am talking about.

import UIKit

class ViewController: UIViewController {


    @IBOutlet weak var topTableConstraint: NSLayoutConstraint!
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var heightConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
    }

    @IBAction func didTapAnimate(_ sender: Any) {

        if heightConstraint.constant != 400{
            //first change the height.  The thing that will happen is allow the tableview to load up all of it's rows and will stop the choppy ness
            heightConstraint.constant = 400
            self.view.layoutIfNeeded()
            //now we can move it up and it will appear to grow.
            //with iphone x i would put a view in inside the safearaa above the table as to now see it slide up or load underneath or you could pin your content view to the safe area and clip to bounds and this would work as well
            topTableConstraint.constant = -heightConstraint.constant
            UIView.animate(withDuration: 1.0) {
                self.view.layoutIfNeeded()
            }
        }else{
           //animating down so let's do the reverse order
            topTableConstraint.constant = -100
            UIView.animate(withDuration: 1.0, animations: {
                self.view.layoutIfNeeded()
            }) { (finished) in
                self.heightConstraint.constant = 100
                self.view.layoutIfNeeded()
            }
        }
    }

}

extension ViewController: UITableViewDataSource {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

}

extension ViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.backgroundColor = UIColor.red
        return cell
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }

}

The result will look something like this. Hope this helps and and explains the issue and solves it for you.

Result

Also if you would like to have all the cells the color red then you would need to register a cell is use dequeueReusableCell instead of creating a new cell.

Upvotes: 1

Amit
Amit

Reputation: 4886

Constraints added to the tableview are conflicting in your scenario. Check that in storyboard.

I used the same code of yours:

@IBAction func methodAnimate(_ sender: Any) {
    heightConstraint.constant = 400
    UIView.animate(withDuration: 1.0, animations: {() in
        self.contentView.layoutIfNeeded()
    })
}

with these constraints :

enter image description here

and the animation is working perfectly fine.

Upvotes: 1

Victor Lagunas
Victor Lagunas

Reputation: 193

Because you use height Constraint.constant = 400, height is for making more big, you need use constraint position 'Y' and move in position Y

  UIView.animate(withDuration: 1.0, animations: {
        self.yourTable.frame = CGRect(x: self.yourTable.frame.origin.x, y: self.view.frame.height - self.yourTable.frame.height - <Where do you want go>, width: self.yourTable.frame.width, height: self.yourTable.frame.height) 
    })

Upvotes: 1

Related Questions