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.
Related
I am currently trying to make a UIView containing some UILabel animate to a new size. But doing so I am having some trouble understanding what is really happening with my view. I read some other post about it but I am still unclear about what is really going on.
In my button I added something that just double the size of the right constraint :
[superView layoutIfNeeded];
rightConst.constant *= 2;
[UIView animateWithDuration:3
animations:^{
[superView layoutIfNeeded];
} completion:nil];
superView Being the view I wanna animate and rightConst the constraint to the right.
But doing so, the animation starts but it is actually coming from left. I don't understand this part. My goal would be to animate just the right side of the view to show the resize and maybe the bottom part of the view but the top left should be fixed.
Thanks.
As described in this document, if you call [aView layoutSubviews], layout of the subviews of aView is forced but layout of aView itself is NOT forced.
You need to call layoutSubviews of the superview of the view you want to animate. In this case, the superview of the superview of the two labels.
Solution is here
UIView *theView;
// theView is the superview of the superview of the two labels.
theView = superView.superview;
[theView layoutIfNeeded];
rightConst.constant *= 2;
[UIView animateWithDuration:3
animations:^{
[theView layoutIfNeeded];
} completion:nil];
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've read many questions that state in order to animate constraint changes, we just need to...
self.someConstraint.constant = 0.f;
[UIView animateWithDuration:0.5f animations:^{
[self layoutIfNeeded];
}];
Recently, I've read that custom view subclasses should use the -(void)updateConstraints method on a UIView to perform constraint updates in case there are many property changes that occur that would cause you to do the above code block in multiple places, which would lead to many layout passes that are unneeded.
UIView -(void)updateConstraints documentation
I actually like this approach where we update all constraints. Some simple code of...
- (void)updateConstraints
{
if(someCondition)
{
self.someConstraint.constant = 0.f;
}
//Maybe some other conditions or constant changes go
here based on the current state of your view.
[super updateConstraints];
}
At this point, my original constraint animation code now only becomes
[self setNeedsUpdateConstraints];
The only problem with this is I've now lost the ability to animate these changes.
I've looked over the view documentation to find a way to animate these changes to the constraints, but I can't seem to find an appropriate method to override to call [self layoutIfNeeded] in an animation block. Even in this case, I feel it should be unnecessary to call that because a layout is going to be performed, and I feel I shouldn't have to force it right then and there.
Does anyone know HOW to cause these constraint changes to animate?
You need to call [self updateConstraintsIfNeeded]. So your code would look like this...
[self updateConstraintsIfNeeded];
[UIView animateWithDuration:0.25 animations:^{
[self layoutIfNeeded];
}];
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.
From the images below, when the user taps on view1 I have view2 and view3 slide down to the footer, being effectively pulled by setting the view3's constraint constant with the footer to 0(ish). I have my xib set up with the constraints as shown in the first image. The 2 most important of these [for me right now] are the view1View2 constraint and the view3Footer constraint
To achieve the slide down I've ended up setting a low priority for the view1view2 constraint and a higher priority for the view3Footer constrain, then updating the view3Footer constraint constant in an animateWithDuration
My problem is getting view2 and view3 to slide back up which, if I was using the same method, I'd achieve by setting the view1view2 constraint constant to 2.
I believe that the problem with the above slide up is the greater priority of the view3Footer constraint over the view1View2 constraint, Priorities seem only to be read only so I can't change these specifically. I understand that in setting constraints I'm only requesting the view positioning.
... I believe I might be using the wrong method entirely ...
Am I going about this in the right way at all? Do I have to get the constraint objects IBOutlet and rewrite them? If so, am I rewriting the priorities? Should I just be using >= for the constants with equal priorities, which doesn't seem to work. My code for simply animating down is below, which isn't much but apart from gesture recognisers, the set up is mainly in the xib
Any help is very much appreciated.
Thanks, Steve
For the slide-down:
[UIView animateWithDuration:0.9 animations:^{
_view3FooterConstraint.constant=2;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){}];
UPDATE Also tried this setting priorities to be equal - can no longer achieve slide-down
_view3FooterConstraint.constant=2;
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.9 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished){}];
For the slide-up:
[UIView animateWithDuration:0.9 animations:^{
_view1View2Constraint.constant=2;
[self.view layoutIfNeeded];
} completion:^(BOOL finished){}];
I think I would do this by just adding and removing the 2 constraints that change (2's top constraint to 1, and 3's bottom constraint to the footer). Make sure all the views have explicit heights, and make IBOutlets to the 2 constraints I mentioned above. You really only need one of those at a time, but you need to add both in IB so you can make outlets to them. In viewDidLoad, I immediately remove the bottom one. This should work in portrait and landscape. The code I used was:
implementation ViewController {
IBOutlet NSLayoutConstraint *conBottom;
IBOutlet NSLayoutConstraint *conTop;
int pos;
}
-(void)viewDidLoad{
[super viewDidLoad];
pos = 0;
[self.view removeConstraint:conBottom];
[self.view layoutSubviews];
}
-(IBAction)togglePosition:(id)sender { //tap recognizer on view one action method
if (pos == 0) {
[self moveDown];
}else{
[self moveUp];
}
}
-(void)moveDown {
[self.view removeConstraint:conTop];
[self.view addConstraint:conBottom];
conBottom.constant=2;
[UIView animateWithDuration:0.9 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
pos = 1;
}];
}
-(void)moveUp {
[self.view removeConstraint:conBottom];
[self.view addConstraint:conTop];
conTop.constant=2;
[UIView animateWithDuration:0.9 animations:^{
[self.view layoutIfNeeded];
}completion:^(BOOL finished) {
pos = 0;
}];
}