I am animating a UITableView. I want the table view to slide open like this:
http://www.youtube.com/watch?v=XIGJLbiRsXE
However, it opens like this:
http://www.youtube.com/watch?v=UJUX-ILCB_0
Notice how the table cells themselves are animating. How can I stop this from happening?
I am using autolayout. The code is thus:
[theView addSubview:theTable];
[theTable reloadData];
[theTable invalidateIntrinsicContentSize];
[UIView animateWithDuration:.33 animations:^{
[theView layoutIfNeeded];
}];
Other details, which may or may not matter:
The table view's parent is a scrollView
I have overridden the intrinsicContentSize of the table view to return the height of all the cells in the table view, so the table view will not scroll
I am changing the datasource of the table view before displaying it
I had a similar problem and was able to stop the animation on the UITableViewCell with the UIView performWithoutAnimation: method.
Use the tableView:willDisplayCell:forRowAtIndexPath: UITableViewDelegate method to get a reference to the cell before it is shown. Then, within the performWithoutAnimation: block, call layoutIfNeeded on the cell object to give it a chance to layout its subviews:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
[UIView performWithoutAnimation:^{
[cell layoutIfNeeded];
}];
}
It might be too late to answer this question but the problem in these kind of cases generally lies in the animation block capturing all the pending layouts scheduled in the next run loop.
The solution that i use is. I call layoutIfNeeded before the animation (before setting or invalidating any constraints) and then inside the animation block.
In your case it will something like this,
[theView addSubview:theTable];
[theTable reloadData];
[theView layoutIfNeeded];
[theTable invalidateIntrinsicContentSize];
[UIView animateWithDuration:.33 animations:^{
[theView layoutIfNeeded];
}];
Have you considered animating instead an opaque subview that is positioned above the table view?
[theView addSubview:theTable];
[theTable reloadData];
UIView *subview = [[UIView alloc] initWithFrame:theTable.frame];
subview.backgroundColor = [UIColor whiteColor];
[theView addSubview:subview];
[UIView animateWithDuration:0.33 animations:^{
subview.frame = CGRectMake(subview.frame.origin.x, subview.frame.size.height, subview.frame.size.width, 0.0);
}];
Try laying out the table first, before you call [theView layoutIfNeeded] inside the animation block. Ideally you could layout piecemeal and for that 0.33 seconds you may get a scroll bar on the table but the goal is to get the table cell layout changes outside of the animated block.
Related
I have UICollectionView cells with a rounded button inside, when I perform reloadData every rounded button goes from the left to the right. To avoid it, I'm using:
[UIView performWithoutAnimation:^{
[self.collectionView reloadData];
}];
but it doesn't seem to work anymore on iOS 14, can someone help me?
This is the correct way to do it:
[UIView performWithoutAnimation:^{
[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
}];
In my tableview I need to animate the UI of every cell as the tableview reloads its data.
This needs to a custom animation, not just a fade or default transition.
For example, When tapping the 'Edit' button, in my view, the tableview reloads and each cell updates its UI for this new edit mode.
However, during this reload I have tried using a UIView animation block to update a constraint on a UILabel in my cells, but it will not animate.
Here is the code I run when I call reloadData on my tableview. This is called in cellForRowAtIndexPath method.
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^
{
//does not animate
_lblContainerLeftConstraint.constant = 18;
[self setNeedsLayout];
//does animate
_reorderIcon.alpha = 0.5f;
} completion:nil];
I think you need to do this in willDisplayCell rather than cellForRowAtIndexPath since that is called just before display which is as close as you can get to the point it actually appears on screen.
Then set the new constraint value before the animation block and also call [cell.contentView setNeedsLayout] to indicate that layout is needed on the cell content.
Finally only add [cell.contentView layoutIfNeeded] and your alpha change in the animation block which should animate the constraint change over your time period.
I am trying to animate change UITableView height constraint using
+ transitionWithView:duration:options:animations:completion:. When the options is set, the UITableViewCell contentView's subview UIView also animates. The effect is like below. As you can see the cell underneath the window goes up and the red dot view's bounds animates to the predefined constraint. If I don't set the animation options, it won't be like this. But I need the animation when I change the UITableView height. So how to keep the table view height animation and disable the table view cell contentView's sub view animation? The code is below. The red dot view is a UIView with a red background color. Is there any way to disable UITableViewCell contentView's subview animation?
- (void)changeTableViewHeight {
self.tableViewTopVerticalSpaceConstraint.constant = 0;
[self.tableView setNeedsUpdateConstraints];
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
self.topViewHeightConstraint.constant = 50;
[self.topView setNeedsUpdateConstraints];
}];
}
//1. All constraints change must be done
self.hightConstraint.constant = 200;
self.anotherConstraint.constant = 34;
...
...
// 2. Indimate to iOS that this view need update constraints.
// Hey iOS I have modified few constraints. beware of it to layout that changes.
[self.view setNeedsUpdateConstraints];
// 3. I'm done, relayout the view with animation,
If I really changed any constraints to view.
[UIView animateWithDuration:1.5 animations:^{
[self.view layoutIfNeeded];
}];
Hope somebody will save their time
Had a similar problem that I solved with disabling animation for the layer of the view I didn't want animated. In your case ... implement the awakeFromNib method for your UITableViewCell subclass and disable the animations there. In your case something like ...
override func awakeFromNib() {
super.awakeFromNib()
redCircleView.layer.removeAllAnimations()
}
The thing is that UIView.animateWithDuration animates the views' layers and for new table view cells and other views created during or because of this animation, all changes will also be animated.
... EDIT ...
In the meantime I managed to find an even better solution. In the options parameter for the UIView.animateWithDuration use the UIViewAnimationOptionLayoutSubviews (or .LayoutSubviews in Swift) option. This layouts the subviews at animation commit time.
update code
- (void)changeTableViewHeight {
self.tableViewTopVerticalSpaceConstraint.constant = 0;
self.topViewHeightConstraint.constant = 50;
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
[self.topView setNeedsUpdateConstraints];
}];
}
I'm using a custom UICollectionview Layout that displays the items in a circle. On a swipe up I reset the constraints on my collectionview and animate those constraint changes. I would like my items to animate with the constraint change so it's one smooth experience.
I get them to animate but only after the constraint change (I think), it looks messy and strange, check the screencast out here: http://cl.ly/ZmA0
This is the code in the swipe gesture method:
- (void)foo:(UISwipeGestureRecognizer *)gesture {
[self.view layoutIfNeeded];
[self.collectionView mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.view.mas_width);
make.height.equalTo(self.collectionView.mas_width);
make.top.equalTo(self.view.mas_top).offset(50);
}];
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
}];
[self.collectionView performBatchUpdates:^{
CircleLayout *layout = (CircleLayout *)self.collectionView.collectionViewLayout;
} completion:nil];
}
Any suggestions?
I'm not sure what MASConstraintMaker is (and the video is not working).
But within the performBatchUpdates block you are getting the collection view layout. What are you doing with it? it unused. Maybe that can point you to the right solution.
I have a UITableView displaying cells. I am using Auto Layout. At some point, I add cells to a section of my tableView and I need to animate a subview of each cells as soon as the cells are visible.
I wrote the following method to animate the content of my cell:
- (void)animateWithPercentage:(NSNumber *)percentage
{
self.progressViewWidthConstraint.constant = [percentage intValue];
[self.progressView setNeedsUpdateConstraints];
[UIView animateWithDuration:0.6f animations:^{
[self.progressView layoutIfNeeded];
}];
}
The method works as expected but I don't know when to call it.
If I call it when - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath is called, I end up animating the whole layout of my cell because the cell did not layout itself before.
I also tried calling it in layoutSubviews but the animation is skipped. I guess at this point it is too early to animate anything.
Any idea where my method should be called?
I've gotten this kind of animation to work by calling performSelector:withObject:afterDelay: in either awakeFromNib or didMoveToSuperview in the custom cell's class. It works even with a delay of 0, but it doesn't work if you just use performSelector. I've noticed that with this method, if some of the cells are off screen, they don't update until they are scrolled on, and the scrolling animation ends. You can get the animation to start as soon as the cell scrolls on screen, even while the scrolling animation is still in progress by using dispatch_async instead.
-(void)awakeFromNib {
dispatch_async(dispatch_get_main_queue(), ^{
[UIView animateWithDuration:2 animations:^{
self.rightCon.constant = 150;
[self.contentView layoutIfNeeded];
} completion:nil];
});
}
Try to put your method in -(void)didMoveToSuperview