I need to animate the insertion of a tableview header view. I want that the table rows to slide down while the header view expands its frame.
So far the best result I go
I found the definitive and proper way to achieve a real animation on tableHeaderView
s!
• First, if you're in Autolayout
, keep the Margin
system inside your header view. With Xcode 8 its now possible (at last!), just do not set any constraint to any of the header subviews. You'll have to set the correct margins thought.
• Then use beginUpdates
and endUpdates
, but put endUpdate
into the animation block.
// [self.tableView setTableHeaderView:headerView]; // not needed if already there
[self.tableView beginUpdates];
CGRect headerFrame = self.tableView.tableHeaderView.bounds;
headerFrame.size.height = PLAccountHeaderErrorHeight;
[UIView animateWithDuration:0.2 animations:^{
self.tableView.tableHeaderView.bounds = headerFrame;
[self.tableView endUpdates];
}];
Note: I only tried in iOS10, i'll post an update when my iOS9 simulator will be downloaded.
EDIT
Unfortunately, it doesn't work in iOS9 as well as iOS10: the header itself does not change its height, but header subviews seem to move as if the header bounds changed.
If you want this kind of animation, that is the solution that I have used: (even though in my case I had also to play a bit with the opacity of the objects, since I was loading another completely different header)
fileprivate func transitionExample() {
let oldHeight = tableView.tableHeaderView?.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height ?? 0
let newHeader = YourCustomHeaderView()
newHeader.hideElements() // If you want to play with opacity.
let smallHeaderFrame = CGRect(x: 0, y: 0, width: newHeader.frame.width, height: oldHeight)
let fullHeaderFrame = newHeader.frame
newHeader.frame = smallHeaderFrame
UIView.animate(withDuration: 0.4) { [weak self] in
self?.tableView.beginUpdates()
newHeader.showElements() // Only if have hided the elements before.
self?.tableView.tableHeaderView = newHeader
self?.tableView.tableHeaderView?.frame = fullHeaderFrame
self?.tableView.endUpdates()
}
}
So, basically, it's all about changing the frame inside the .animate
closure. The beging/end updates is needed in order to move and update the other elements of the tableView.
Here is a Swift 5 UITableView extension for changing the headerView or changing its height, animated using AutoLayout
extension UITableView {
func animateTableHeader(view: UIView? = nil, frame: CGRect? = nil) {
self.tableHeaderView?.setNeedsUpdateConstraints()
UIView.animate(withDuration: 0.2, animations: {() -> Void in
let hView = view ?? self.tableHeaderView
if frame != nil {
hView?.frame = frame!
}
self.tableHeaderView = hView
self.tableHeaderView?.layoutIfNeeded()
}, completion: nil)
}
}
Use below code this works
extension UITableView {
func hideTableHeaderView() -> Void {
self.beginUpdates()
UIView.animate(withDuration: 0.2, animations: {
self.tableHeaderView = nil
})
self.endUpdates()
}
func showTableHeaderView(header: UIView) -> Void {
let headerView = header
self.beginUpdates()
let headerFrame = headerView.frame
headerView.frame = CGRect()
self.tableHeaderView = headerView
UIView.animate(withDuration: 0.2, animations: {
self.tableHeaderView?.frame = headerFrame
self.tableHeaderView?.alpha = 0
self.endUpdates()
}, completion: { (ok) in
self.tableHeaderView?.alpha = 1
})
}
}
have the header frame at CGRectZero
and set its frame using animation
[self.tableView beginUpdates];
[self.tableView setTableHeaderView:self.someHeaderView];
[UIView animateWithDuration:.5f animations:^{
CGRect theFrame = someBigger.frame;
someHeaderView.frame = theFrame;
}];
[self.tableView endUpdates];
Here's what I got to work in Swift, with an initial frame height of zero and updating it to its full height in the animations closure:
// Calculate new frame size for the table header
var newFrame = tableView.tableHeaderView!.frame
newFrame.size.height = 42
// Get the reference to the header view
let tableHeaderView = tableView.tableHeaderView
// Animate the height change
UIView.animate(withDuration: 0.6) {
tableHeaderView.frame = newFrame
self.tableView.tableHeaderView = tableHeaderView
})