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 got is by using the following code:
[self.tableView beginUpdates];
[self.tableView setTableHeaderView:self.someHeaderView];
[self.tableView endUpdates];
The problem with this solution is that the header itself doesn't get animated, its frame doesn't expand.
So the effect I'm getting is that the table rows slide down (as I want) but the header view is immediately shown, and I want it to expand it's frame with animation.
Any ideas?
Thanks.
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
})
The selected answer didn't work for me. Had to do the following:
[UIView animateWithDuration: 0.5f
animations: ^{
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = self.headerViewController.preferredHeight;
self.tableView.tableHeaderView.frame = frame;
self.tableView.tableHeaderView = self.tableView.tableHeaderView;
}];
The really peculiar part is that this works in one view but not in a subclass of the same view controller / view hierarchy.
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.
SWIFT 4 SOLUTION w/ Different Header
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.
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
})
}
}