Following on from a previous SO question which I asked, I am attempting to animate the change in height of a UICollectionView (which is yellowBox in the example). This change in height is being triggered by modifying the auto layout constraints on the collection view with the following code:
- (IBAction)expandYellowBox:(id)sender {
[UIView animateWithDuration:0.5
animations:^{
self.yellowBoxHeightConstraint.constant += 50;
[self.view layoutIfNeeded];
}];
}
However, when I call [self.view layoutIfNeeded] this results in the collection view being reloaded, so it flashes visibly to the user. However, I don't want the collection view to reload but instead just to animate its height change. Is there any way to either avoid -layoutIfNeeded reloading the collection view, or an alternative way to animate the constraint height change which doesn't call a method which has the side-effect of reloading the collection view?
In case anyone else encounters this problem, it was because I was actually calling [self.collectionView reloadData] a few lines above the animation block (and didn't notice!). It seems that calling -reloadData and -layoutIfNeeded causes the collection view to flash. Removing the call to -reloadData resolves the problem.
Related
I'm having a problem with auto layout in a collection view cell in iOS 9.
During the collection view's :cellForItemAtIndexPath: I change a constraint:
[[self rootStack] setDistribution:UIStackViewDistributionFillEqually];
or
[[self rootStack] setDistribution:UIStackViewDistributionFillProportionally];
During the cell's preferredLayoutAttributesFittingAttributes: I try to force an additional round of layout:
- (UICollectionViewLayoutAttributes*) preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
[self setNeedsLayout];
[self layoutIfNeeded];
return layoutAttributes;
}
Still, when cells are displayed, some show the effect of the constraint change and some do not. If I scroll a cell that does not display the change offscreen and then scroll back to it, it then displays the change. Moving the constraint changes to the cell's updateConstraints resulted in none of the cells showing the change on first display.
I can fix this for the first cells that are displayed by calling reloadData on the collection view during the collection view controller's viewWillAppear:. But once I start scrolling some of the cells that come into view are not formatted correctly (unless I scroll past them and then scroll back to them).
Is there a way I can make sure that all cells are formatted correctly the first time they are displayed?
I want to change my width constraint of View. I have connected the width with IBOutlet inside my View Controller. However, it does not work.
Try to animate it what's going on, actually the View keep going back to its original position.
Here is the code:
_providerExpSearchInputWidth.constant = 1000.0f;
[_providerExpSearchInput layoutIfNeeded];
Anyone can advise?
You should have your view tell autolayout to layout its subviews immediately first so that your layout constraints are completely known. Then you should set a new constraint and in your animation block you will again tell autolayout to layout its subviews immediately. Since you're making this call in the animation block, all of the changes will be animated.
You can try something like this:
[self.view layoutIfNeeded];
_providerExpSearchInputWidth.constant = 1000.0f;
[UIView animateWithDuration:4.0 animations:^{
[self.view layoutIfNeeded];
}];
This is from the Apple Documentation on layoutIfNeeded:
Use this method to force the layout of subviews before drawing. Using the view that receives the message as the root view, this method lays out the view subtree starting at the root.
I have a UITableView added to a UIViewController inside a UIView. The table view has constraints to fit the size of it's parent view. The view has constraints for it's position and size. If I animate the size of this view, the table view shows new rows, but the new elements, which about to appear, seem to fly around from their initial (not set) position. For example, the right detail indicator of the UITableViewCell flies from the left edge of the cell, the text labels from a slightly different position, etc.
I do the animation like this:
[self.tableView reloadData];
[UIView animateWithDuration:0.3f animations:^{
self.tableViewHeightConstraint.constant = blah;
[self.view layoutIfNeeded];
}];
As you can see, the data reloading takes place before the animation block. Once the animation was played, changing the view size does not animate the table view's contents anymore (I guess it does, but everything is in their place already.)
How can I prevent animating the elements and keep the animation of the frame?
Have a look at calling setAnimationsEnabled on the cells or maybe the cells accessory view.
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 need to reload tableview, but in somehow I want to keep the scroll position. Any way to do it? Maybe store content offset and in viewDidLayoutSubviews I should reset it?
TableView does not reset scroll position. It maintains content offset even after reloadData function is called. So no need to do anything, if you want to keep pixel position.
If you are changing row height at the time of reloading data or changing datasource then you can use scrollToRowAtIndexPath:atScrollPosition:animated: method.
You can save your current offset before reloading the table use this method to save your current offset
self.tableView.contentOffset.y
Or you can Reload like this it will Reload your table from your current Offset
Try this:
[UIView animateWithDuration:0.1 delay:0 options:0 animations:^{
self.tableView.contentOffset = CGPointMake(0, currentOffset);
} completion:^(BOOL finished) {
[self.tableView reloadData];
}];
After reloading the table view you need to call the TableView's method scrollToRowAtIndexPath:atScrollPosition:animated:
Just provide the index path, scroll position which could be one of the following
UITableViewScrollPositionNone,
UITableViewScrollPositionTop,
UITableViewScrollPositionMiddle,
UITableViewScrollPositionBottom
and YES/ NO in animated parameter.
just call scrollToRowAtIndexPath:atScrollPosition:animated: before you use reloadData (with animation:NO)
Also, be carefull in case you have less cells in the updated tableview to prevent crashes.
Hope it helps