Reputation: 8579
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.
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:
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
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..
Here is an image of the entire setup.
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.
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
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 :
and the animation is working perfectly fine.
Upvotes: 1
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