I want to move a view from one postion to another, I can implement it using
self.view.center = CGPointMake(100, 200);
however, if the project is using Autolayout, then the view will be back to original position after running:
[self.view.superview setNeedsLayout];
then how to actually move a view to new position?
With AutoLayout enabled, we should FORGET FRAMES and only CONSIDER CONSTRAINTS.Yes, for animating also you can no longer change the frame or center, view's will revert back to their original position when layout is called.
Instead you should consider changing the constant value of the constraint to get the same effect.
Consider a User Interface like the image given below.I have a image view with a 20 points leading space from it's superview that means it has a horizontal space constraint with it's superview.Also I have three more constraints attached to that image view, top, width and height.
I will show you how we can animate the image from left to right as shown in the image.
Create IBOutlet's for the constraint's we need to animate.Here we are taking only horizontal space constraint, that is enough to move this view from the left to right.
#property (weak, nonatomic) IBOutlet UIImageView *imageView;
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *horizontalSpaceConstraint;
Inside Go Away action, we need to update the constant value of this constraint.
- (IBAction)moveFrontAction:(id)sender {
self.horizontalSpaceConstraint.constant = 220;
[UIView animateWithDuration:0.5 animations:^{
[self.imageView layoutIfNeeded];
}];
}
Now the view should be moved to the right end.I'm just doing that inside a animation block so we will be able to see a nice animation from left to right and vice versa.In Production, we should not hard code the values like this.Just doing it here to make the concept clear.
Inside the Come Back action, we are again resetting the constant back to it's original value, so you can see the orange image animating back to the original location.
- (IBAction)moveBackAction:(id)sender {
self.horizontalSpaceConstraint.constant = 20;
[UIView animateWithDuration:0.5 animations:^{
[self.imageView layoutIfNeeded];
}];
}
You must change the constraints if you are using autoLayout. The way that is suggested is to make an outlet in your view controller of the constraint, then you change the constant of the constraint. If you have the time i would definitely recommend going here and watching "Auto Layout by Example" or "Best Practices for Mastering Auto Layout". They helped me out a lot. I guess the point to take away is that with autoLayout, you no longer think in terms of frames. So setting the center just doesnt work with auto layout. It's all about how views are related to each other.
Related
I have a UIView called containerView.
I add this as a subview to a controller view's root view. I have programmatically added a few constraints to it (I centered it and made the width a few points from the superview's width).
I have added a few UILabels to the containerView as subviews. The height of the UILabels dictate the height of the containerView.
When the user taps the screen, the containerView is moved up from CGRectOffset() and once the animation is complete, it is moved back to the original position.
CGPoint absolutePoint = self.containterView.frame.origin;
self.containerYConstraint.constant = -absolutePoint;
[UIView
animateWithDuration:0.5
animations:^
{
[viewForUpdate setNeedsUpdateConstraints];
}
completion:^(BOOL finished)
{
self.containerYConstraint.constant = 0;
[viewForUpdate setNeedsUpdateConstraints];
[self.containerView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)]; // Remove all subviews
}];
I need to remove the UILabels I have put in there as subviews and replace them with different labels. However, the moment I remove one of the UILabel's the entire containerView goes missing (I set the background as red so I can see it). I remove all the subviews in the example but when I try to just remove one the same effect occurs.
Why does this occur? Does this have something to do with auto layout? Also if I want to recenter it after I remove one of the UILabels, how do I re-do the constraints?
Modifying frames when using AutoLayout is a no-no. Once you begin using AutoLayout you're effectively telling the system that you want it to set the frames for you.
Instead of animating the frame directly, create properties pointing to your constraints and animate those constraints.
Say that I have a project which looks like the following:
There are two UIViews - one called yellowBox and the other called redBox. The auto layout constraints dictate that the yellowBox is 60 points from the top of the superview with 350 points leading and trailing spaces (i.e. to left and right of the view). The redBox has the same leading and trailing space constraints. There is a vertical space constraint between the two boxes of 0 to indicate that the bottom of the yellowBox should always be directly on top of the redBox.
When the user taps the Expand Yellow Box button, I would like the height of the yellowBox to increase and, as a result, the redBox moves down to continue satisfying the vertical space constraint (that the yellowBox is always on top of the redBox). Here is the animation code:
- (IBAction)expandYellowBox:(id)sender {
[UIView animateWithDuration:0.5
animations:^{
CGRect newFrame = self.yellowBox.frame;
newFrame.size.height += 50;
self.yellowBox.frame = newFrame;
}];
}
However, I have been unable to get this working properly. As you can see by the red icon, there is a problem with the constraints at the moment:
which is fair enough, as there's no way for auto layout to know the height of the boxes. However, I don't know how to resolve this issue in such a way that it will allow for the boxes to be resized and moved. For example, if I specify a height constraint on the yellowBox then that will prevent it from being resized. If I set the height constraint to be greater than or equal (to allow the yellowBox height to increase) then I get a different constraint error:
All constraints have been established using Xcode in the storyboard - none have been written in code.
Any help greatly appreciated.
EDIT: As it turns out, the yellowBox is growing when the button is clicked - it's just I couldn't tell because it appears behind the redBox. I only noticed after clicking it around four times and it started appearing out the bottom of the redBox. However, the same question still remains - how to get the redBox to always stick to the bottom of the yellowBox.
Try it as mentioned by shwet-solanki, but add the following line after changing the constraint:
[self.view layoutIfNeeded];
the IBAction would look like:
- (IBAction)expandYellowBox:(id)sender {
[UIView animateWithDuration:0.5
animations:^{
self.yellowBoxHeightConstraint.constant += 50;
[self.view layoutIfNeeded];
}];
}
Where yellowBoxHeightConstraint is the IBOutlet for height constraint of yellowBox.
Hope this helps.
Add Height constraint to both the views
Create an IBOutlet for height constraint of yellowbox
Now instead of changing the height of yellowbox in the button pressed event, rather change the value of the height constraint. i.e suppose your IBOutlet constraint name is yellowBoxHeightConstraint, then yellowBoxHeightConstraint.constant += 50;
Hope this works for you.
//
// WPViewController.h
// AutoLayoutTEst
//
// Created by VASANTH K on 09/01/14.
//
//
#import <UIKit/UIKit.h>
#interface WPViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button1;
- (IBAction)buttClicked:(id)sender;
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *heightConstrain;
#property (strong, nonatomic) IBOutlet UIButton *button2;
#end
click Event
- (IBAction)buttClicked:(id)sender {
self.heightConstrain.constant=100;
}
here u need to set the heightConstrain for the Yellow button then create reference outlet for that button then update the heightConstrain to update the size of that button it will automatically move your Red button down.
https://github.com/vasanth3008/AutoLayoutHeighDemo
refer my sample demo project
I have a table view with a textfield on the left hand side of a cell and on the right hand side there is a label.
The constraints of the label are very fixed, with a fixed width, vertical center of the container and fixed distance to the right edge of the container.
The constraints of the textfield are variable. It has a fixed position to the left side of the container, also vertical center of the container and a fixed distance to the right edge of the container. But since no width is set, the width is actually variable, depending on the device my app is used.
Now, in edit mode I hide the label and only show the textfield. So I would like to decrease the spacing of the textfield to the right end of the container since no label is in the way. But how can I do this in code? All constraints are set in XCode and I have no experience in doing this with code...
Edit: Screenshot
If I understand your question correctly, you could setup an IBOutlet from your right (Trailing) constraint to your UITableViewCell. Then, in your UITableViewCell, change the constant of the right (Trailing) constraint.
Ex.
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *textFieldTrailingSpaceToViewConstraint;
[self layoutIfNeeded];
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
[textFieldTrailingSpaceToViewConstraint setConstant: 100];
[self layoutIfNeeded];
} completion:nil];
I have a UIViewController with a UICollectionView and a UIView at the bottom. The way I put it together is displayed in the image below
The yellow square is the UICollectionView and the red is the UIView. This works out just fine. But now I want to resize the UIView because it sometimes contains more info and needs to be bigger. So I tried this:
[self setFrame:CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height + 10)];
But this expands the UIView at the bottom and it is not visible. I guess this is because I do not have the correct constraints? I also tried to subtract the origin.y with the same amount and this works only the UICollectionView doesn't get resized with the new height. So how do I tackle this problem?
If you are using autolayout, you should not be setting the frame from your code. Instead you should modify the constant of a constraint that is causing your view to be the incorrect size. You can have an IBOutlet to that constraint and you can change it's constant property. Then call setNeedsLayout on your view controller's view
When setting constraints on your storyboard or in a xib file, animations perform animations on the constraints instead of the sizes and positions.
First create a outlet reference of the constraint which will change (in your case the top space of your UIView to the top layout guide) in the header file of your view controller.
When you want to animate a view, you now have to update its constraints and ask to layout the views.
For example :
[UIView animateWithDuration:0.2
animations:^{
viewYConstraint.constant -= 44;
[self.view layoutIfNeeded];
}
]
//Now don't forget to update constraints
[self.view updateConstraints];
I've never worked with autolayout constraints before. I have a small new app I'm working on and noticed that the NIB's views are defaulting to autolayout. So, I figured I'd take the opportunity to work with it and try to figure out where Apple is going with this.
First challenge:
I need to resize an MKMapView and I'd like to animate it to the new position. If I do this the way I'm used to:
[UIView animateWithDuration:1.2f
animations:^{
CGRect theFrame = worldView.frame;
CGRect newFrame = CGRectMake(theFrame.origin.x, theFrame.origin.y, theFrame.size.width, theFrame.size.height - 170);
worldView.frame = newFrame;
}];
...then the MKMapView will 'snap' back to its original height whenever a sibling view gets updated (in my case a UISegmentedControl's title is being updated [myUISegmentedControl setTitle:newTitle forSegmentAtIndex:0]).
So, what I think I want to do is change the constraints of the MKMapView from being equal to the parent view's hight to being relative to the top of the UISegmentedControl that it was covering: V:[MKMapView]-(16)-[UISegmentedControl]
What I want is for the MKMapView height to shorten so that some controls beneath the map view are revealed. To do so I think I need to change the constraint from a fixed full size view to one where the bottom is constrained to the top of a UISegmentedControl...and I'd like it to animate as view shrinks to new size.
How does one go about this?
Edit - this animation is not animating though the bottom of the view does move up 170 instantly:
[UIView animateWithDuration:1.2f
animations:^{
self.nibMapViewConstraint.constant = -170;
}];
and the nibMapViewConstraint is wired up in IB to the bottom Vertical Space constraint.
After updating your constraint:
[UIView animateWithDuration:0.5 animations:^{[self.view layoutIfNeeded];}];
Replace self.view with a reference to the containing view.
This works for me (Both iOS7 and iOS8+). Click on the auto layout constraint you would like to adjust (in interface builder e.g top constraint). Next make this an IBOutlet;
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;
Animate upwards;
self.topConstraint.constant = -100;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:1.5 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
Animate back to original place
self.topConstraint.constant = 0;
[self.viewToAnimate setNeedsUpdateConstraints];
[UIView animateWithDuration:1.5 animations:^{
[self.viewToAnimate layoutIfNeeded];
}];
There is a very good tutorial from apple itself that explain how to use animation with autolayout.
Follow this link and then find the video named "Auto layout by example"
It gives some interesting stuff about autolayout and the last part is about how to use animation.
I have made this small demo available. It shows how auto-layout constraints can be changed and animated in a very simple example. Simply take a look at the DemoViewController.m.
Most people use autolayout to layout items on their views and modify the layout constrains to create animations.
An easy way to do this without a lot of code is creating the UIView you want to animate in Storyboard and then creating a hidden UIView where you want the UIView to end. You can use the preview in xcode to make sure both UIViews are where you want them to be. After that, hide the ending UIView and swap the layout constraints.
There is a podfile for swapping layout constrains called SBP if you don't want to write it yourself.
Here's a tutorial.
No need to use more IBOutlet reference of the constraint instead of this you can directly access or update already applied constraint either applied by Programmatically or from Interface Builder on any view using the KVConstraintExtensionsMaster library. This library is also managing the Cumulative behavior of NSLayoutConstraint.
To add Height Constraint on containerView
CGFloat height = 200;
[self.containerView applyHeightConstrain:height];
To update Height Constraint of containerView with animation
[self.containerView accessAppliedConstraintByAttribute:NSLayoutAttributeHeight completion:^(NSLayoutConstraint *expectedConstraint){
if (expectedConstraint) {
expectedConstraint.constant = 100;
/* for the animation */
[self.containerView updateModifyConstraintsWithAnimation:NULL];
}
}];