I want to perform the following constraint animation on a magnify icon
At the beginning I want my icon to be at the right of my screen and then move it to the left of my textfield :
___________________
|___________________| O-
to
___________________
O- |___________________|
So what I did is setup a constraint A on the icon to the right of my superview at priority 1000
and add another constraint B on the icon to the left of my textfield at priority 750
then when I want to perform my animation I remove the constraint A so I expected the icon to move to the left of my texfield but it didn't move
How to do that?
constraint A:
Constraint B:
Some code of the animation :
[UIView animateWithDuration:0.5 animations:^{
self.view.alpha = 1;
} completion:^(BOOL finished) {
//animating the icon
[self.view removeConstraint:_loupeSuperviewLeftMargin];//remove constraint A
[self.view setNeedsUpdateConstraints];
UIView animateWithDuration:0.5f animations:^{
[self.view invalidateIntrinsicContentSize];
[self.view layoutIfNeeded];
}];
}];
It worked for me if I gave the icon (I used a button for my test) these 2 constraints to the label (the lower priority constraint could have any priority up to 749 -- if I use 750, both views disappeared),
Then in code, I removed the high priority one,
- (IBAction)moveButton:(id)sender {
[self.view removeConstraint:self.rightCon]; // rightCon is the high priority constraint
[UIView animateWithDuration:1 animations:^{
[self.view layoutIfNeeded];
}];
}
Be sure that you call removeConstraint on whatever view is the superview of the two views that have the spacing constraint between them -- it is that superview that owns that constraint.
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 have a view controller that contains two views. What I need is that after the view controller has been pushed, the view on top (_sidepanelview) moves to the left disappearing of the screen.
Here is what I wrote:
-(void)viewDidAppear:(BOOL)animated{
CGRect newsidepanelviewposition = _sidepanelview.frame;
newsidepanelviewposition.origin.x = -_sidepanelview.frame.size.width;
[UIView animateWithDuration:0.5 delay:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
_sidepanelview.frame = newsidepanelviewposition;
} completion:^(BOOL finished) {
NSLog(#"Done!");
}];
}
The problem is that when I run the app, after the view controller been pushed, the view _sidepanelview disappears from the screen and appears again to the center of the screen coming from the right side instead of moving to the left from x=0 to x= -_sidepanelview width disappearing from the screen.
What I'm doing wrong?
Note: I did the app with auto layout unchecked and the animation worked fine, but using it with the auto layout turned on it freaks out!
As your note suggests, Auto Layout is the "culprit".
Your views have constraints that are periodically enforced by the AutoLayout engine. When you alter the frame of a view, the frame changes, but not the view's layout constraints. When the next cycle of layoutSubviews occurs, your views position and size (i.e. frame) will be reset to what the constraints dictate.
If you want to include AutoLayout for that view/viewcontroller, use constraint changes to perform the animation.
The golden rule here is, setFrame is the antithesis of AutoLayout.
Sample code
-(void)viewDidAppear:(BOOL)animated {
_sidePanelLeadingSpace.constant = -_sidepanelview.frame.size.width; // 1
[UIView animateWithDuration:0.5 delay:1.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
[self.view layoutIfNeeded]; // 2
} completion:^(BOOL finished) {
NSLog(#"Done!");
}];
}
Have a reference, _sidePanelLeadingSpace (NSLayoutConstraint), to the leading space constraint of _sidepanelview, for e.g., as an IBOutlet.
Alter the constraint's constant and layout the view by calling layoutIfNeeded in an animation block
I want to animate from a height of 0 to the height of the text (UILabel). I am using autolayout and I do not know how high the text will be. My approach was to start by setting a height=0 constraint to the text, and animate like this:
//retrieves the height constrain of the clicked item
NSLayoutConstraint *heightContraint = [heightConstraints objectAtIndex:sender.tag];
//activates/deactivates the constraint
heightContraint.active = !heightContraint.active;
//animates
[UIView animateWithDuration:3 animations:^{
[self layoutIfNeeded];
}];
My problem is, that with this approach, the text height does not animate, it changes from a height of 0 to the new height instantly. Only the position/size change of the containing views is animated. How I animate the text height change without knowing the height of the text??
I have found a solution. Instead of setting a constraint to the height, I set a containing view (clipping), which contains the UILabel and the view above. Then, I create 2 constraints that pin the bottom edges to the containing view, one related to the UILabel, and one related to the view above. I just activate/deactivate those constraints like this:
//get the constraints
NSLayoutConstraint *viewAboveTextConstraint =
[viewAboveTextConstraints objectAtIndex:sender.tag];
NSLayoutConstraint *uilabelContraint = [uilabelContraints objectAtIndex:sender.tag];
//flip the active states
viewAboveTextConstraint.active = !viewAboveTextConstraint.active;
uilabelContraint.active = !uilabelContraint.active;
//animate
[UIView animateWithDuration:.3 animations:^{
[self layoutIfNeeded];
}];
The animation by itself works when I use a constraint on the distance between the UIImageView and the top of the parent UIView without vertical and horizontal centering.
The constraints on the UIImageView:
Width: 240
Height: 128
Top space to Top Layout: 200 --> connected to logoImageViewVerticalSpaceConstraint
The animation works great when I use this:
self.logoImageViewVerticalSpaceConstraint.constant = 20;
[UIView animateWithDuration: 1.0
delay: 0
options:(UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction)
animations:^{[self.logoImageView layoutIfNeeded];}
completion:^(BOOL finished) {NSLog(#"Finished animation"); }];
The issue kicks in when I don't want to use a static space to top layout constraint because the imageview needs to be centred for 3.5 and 4 inch devices. To solve this I thought starting off with a vertical and horizontal center constraint.
Width: 240
Height: 128
Align center x to: superview --> connected to logoImageViewYCenterConstraint
Align center y to: superview
Now I thought I could simply remove the y center constraint and adding the space to top layout constraint of 20pt myself:
[self.logoImageView removeConstraint:self.logoImageViewYCenterConstraint];
NSLayoutConstraint *topSpaceConstraint = [NSLayoutConstraint
constraintWithItem:self.view attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual toItem:self.logoImageView
attribute:NSLayoutAttributeTop multiplier:1
constant:20];
[self.view addConstraint:topSpaceConstraint];
[UIView animateWithDuration: 1.0
delay: 0
options:(UIViewAnimationOptionCurveEaseInOut|UIViewAnimationOptionAllowUserInteraction)
animations:^{
[self.logoImageView layoutIfNeeded];
}
completion:^(BOOL finished) {
NSLog(#"Finished animate");
}];
The result is that the UIImageView stays centred and it blows up a bit.
Which is the total opposite of what I expect to happen because I removed the x center constraint. Added the top constraint with distance 20pt to the top of the parent UIView and I did not touch any other constraints (like the width and height).
An NSLayoutConstraint has one or two views that it constrains. In your case, the views are self.logoImageView and self.view.
To have any effect, the constraint must be installed on a view, and not just any view. The constraint must be installed on a common ancestor of both constrained views. (Note that a view is considered to be an ancestor of itself.) And when you remove a constraint, you must remove it from the view on which it is installed.
You're trying to remove the centering constraint from self.logoImageView, but the constraint can't have been installed on that view.
As of iOS 8.0, the preferred (and easiest) way to uninstall a constraint is to set its active property to NO:
self.logoImageViewYCenterConstraint.active = NO;
Prior to iOS 8.0, you have to remove it from the view where it's installed. The constraint is probably installed on self.view. So try this instead:
[self.view removeConstraint:self.logoImageViewYCenterConstraint];
Note also that if you want to animate the image view back to the center, you'll need to uninstall topSpaceConstraint and reinstall self.logoImageViewYCenterConstraint.
A different way to handle this is to install both constraints simultaneously, but give one of them a lower priority. When you need to change the position of the image view, change the priorities of the constraints. Thus:
- (void)viewDidLoad {
[super viewDidLoad];
if (self.topSpaceConstraint == nil) {
self.topSpaceConstraint = [NSLayoutConstraint constraintWithItem:...];
self.topSpaceConstraint.priority = 1;
[self.view addConstraint:self.topSpaceConstraint];
}
}
- (void)setImageViewCenteredVertically:(BOOL)isCentered animated:(BOOL)animated {
if (isCentered) {
self.topSpaceConstraint.priority = 1;
self.logoImageViewYCenterConstraint.priority = UILayoutPriorityRequired;
} else {
self.topSpaceConstraint.priority = UILayoutPriorityRequired;
self.logoImageViewYCenterConstraint.priority = 1;
}
if (animated) {
[UIView animateWithDuration:1 delay:0
options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
animations:^{
[self.view layoutIfNeeded];
}
completion:nil];
}
}
I'm not really a master of NSLayoutConstraints, but either you should add the constraint to the logoImageView object or you should change the constraintWithItem: parameter because to me something looks strange right now. You're removing a constraint from the imageView, then adding a constraint to the self.view object which refers to self.view item
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;
}];
}