I have a nested view that I animate out of the frame completely on a certain action. To do this, I update the constants for the constraints in an animation block.
[UIView animateWithDuration:.5 animations:^{
self.categoriesTableViewConstraintToBottom.constant = [UIScreen mainScreen].bounds.size.height;
self.categoriesTableViewConstraintToTop.constant = [UIScreen mainScreen].bounds.size.height;
[self.view layoutSubviews];
}, completion: nil];
Basically the two constraints just tie it to the top and bottom of the parent view (which is full screen). Obviously I can't change one of these constraints without changing the other, otherwise the layout isn't satisfiable.
The weird part here is I have to change these in the order I showed above. If I update the constants in the other order, it throws an 'satisfy' error.
So:
Why does the order matter?
Are we technically supposed to remove constraints while changing them if they will conflict until we update all the constraints? (Are they evaluated immediately for some reason instead of waiting for layoutSubviews or layoutIfNeeded?
Related
I am very new to iOS development and have never done auto layout before ,I have actually seven buttons on a view controller that needs to look round on every screen without fixing height and width....I have looked many tutorials but couldn't understand that how I can add constraints on those round buttons and show them at same position on every screen. I want the buttons to actually increase size when screen increase and decrease when screen size decreases.Please help and show which constraints should be added.!this shows how buttons are added on my view controller
If you want perfect round buttons(circle) then width and height should be same. For that set the aspect constraint with multipler 1:1 so that width and height will become equal.
Based on the the screenshot you have provided, see below how the constraints should look like:
Well, two points:
Position: Well, you need understand accurately ‘same position in every screen’, I guess you know view.frame = CGRectMake(10, 20, 50, 50)but same code not lead to 'same position' in different screen, important thing is which way you want. Think about a increasing screen, you have a square on it, what do you want this square change? Different change style lead to different code.
Size: You said you want square increasing or decreasing with screen, the basic way is let square.width && square.height changing with screen, if use frame layout you may write view.frame = CGRectMake(10, 20, SCWidth * 0.0666, SCHeight * 0.0833), certainly autoLayout support scale calculate, I recommend you use Masonry to add layout, sample code like:
[square mas_remakeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(self.mas_width).multipliedBy(0.0083);
}];
of course if you use xib to do it, you can see constraints have multiplier property to fix problem.
Use this code to make round button..
You can programmatically get the current height of Button and then assign the half of height to corner radius to make it round.
[self.view layoutIfNeeded];
[self.view setNeedsLayout];
self.yourButton.layer.cornerRadius = self.yourButton.frame.size.height/2;
self.yourButton.clipsToBound = YES;
I have a controller in storyboard that consist of two container views. I set the vertical distance between the two containers to zero.
What I want is to change the height constraint of one of the container at run time.
Here is the code for changing the constraint:
[UIView animateWithDuration:kAnimationDuration animations:^{
self.offeringContainerHeightConstraint.constant = [SJDataManager shared].offeringItems.count * kOfferingCellHeight + kOfferingHeaderHeight;
[self.view layoutIfNeeded];
}];
The issue is, the other container view moves before so there is blank area between the two containers until the animation completes.
I want that two container views should change their constraint value in synchronisation so that this flicker can be removed.
I have finally found the solution for this. Actually it was the difference between the expectedHeightForRowAtIndexPath and heightForRowAtIndexPath. As the height returned by these two methods are different that is why I am getting the flicker.
Is it possible to apply such auto layout constraints with aspect ratio calculated on the fly based on intrinsicContentSize?
In the documentation I've found only constrains with fixed ratio value.
Actual with and height from intrinsicContentSize is not important in my use case, I want preserve height and width ratio of the view which changes dynamically.
Should I provide my own implementation of the constraint? Or is there a better way?
The intrinsicContentSize is not available as input to constraints. (Logically, there are constraints that implement the intrinsicContentSize and related content-hugging and compression-resistance priorities, but that's different.)
If you want such an aspect ratio constraint, you'll have to add it yourself. It's easy enough to query the intrinsicContentSize at a given moment, verify that it provides real values (not NSViewNoInstrinsicMetric) for both dimensions, compute the aspect ratio, and then use that as the multiplier in a constraint that relates the item's width to its height.
The hard part is knowing when the intrinsicContentSize has been invalidated so you can remove the old aspect ratio constraint and add a new one. You can do that as a subclass of the view by overriding -invalidateIntrinsicContentSize. (Be sure to call through to super!) However, I don't know of a way to do that from a controller or superview.
You can find the answer on how to set up ratio-based constraints here. All you need is to constrain the width and the height together, maintaining a given aspect ratio (in that case 4/3).
We can debate whether it's a good thing views know this information or whether should their parents set this kind of constraints. I usually prefer parents to set constraints, but if your view doesn't make any sense without this constraint or all these views need this constraint, you can safely let these views manage their own width/height ratio.
Finally, intrinsicContentSize tells Auto Layout the size of the view when the view is alone:
Returns the natural size for the receiving view, considering only properties of the view itself.
I don't know what your view represents, but as the documentation says, you can return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric):
If a custom view has no intrinsic size for a given dimension, it can return UIViewNoIntrinsicMetric for that dimension.
If you have the situation where you want to calculate the value of a constraint during runtime, the best option is to CTRL drag the constraint into the .h file of your controller and create an IBOutlet for it. This allows you to change the value of a constraint in code. You can even animate the change to a constraint value.
Then in your code at setup time or when an action occurs which might change the value you want (like loading a new image for example) you calculate the value you want for the constraint and set its value in the code. Usually you do this using:
self.myConstraintIBOutlet.constant = <insert your new value>;
You may then need to mark the affected view as needing layout:
// Mark whole view as needing layout. You could do this in a subview if
// only a small area is affected
[self.view setNeedsLayout];
[self.view layoutIfNeeded];
If you want a smooth transition, you can put layoutIfNeeded inside an animation block causing it to animate the change of constraint:
[self.view setNeedsLayout];
[UIView animateWithDuration:.25 animations:^{
[self.view layoutIfNeeded];
} completion:^(BOOL finished) {
// Completion code
}];
This is the setup:
A UIView created on Interface Builder, linked to an IBOutlet variable (_vAbout)
A constraint for this view that we want to animate, linked to an IBOutlet variable (_ctrBottomAboutView)
I am using this code to animate:
_ctrBottomAboutView.constant = -100;
[UIView animateWithDuration:0.5 animations:^{
[_vAbout layoutIfNeeded];
}
My problem is: whenever the view has any subviews in it, the animation doesn't work. However, if the view has no children, the animation works correctly.
Do you have any idea of a solution? I have tried everything: adding and removing constraints instead of modificating the constant value, adding constraints to the subviews on Interface Builder...
After some experiments starting from the ground with an empty project, this is what I've found:
Given A the view we want to animate and B its superview
It's very important to keep in mind that the view that receives the layoutIfNeeded message is the view that owns the constraint.
In the case of NSLayoutAttributeWidth and NSLayoutAttributeHeight the owner of the constraint is actually A, but in all the other cases, the view that owns the constraint is B
HOWEVER
If A does not have any subviews, we can call [A layoutIfNeeded] at any time on our code and it will work
If A has one or more subviews but we start the animation on viewDidLoad we can call [A layoutIfNeeded] and it will work
Disclaimer: I'm comparably new to iOS development. This is my very first try on Auto-layout.
I have a container UIView that contains one UIImageView and a UILabel
When I click on the UIButton in the bottom of the view controller, I'd want the UIImageView to disappear and UILabel to automatically go to the top.
P.S. Please introduce me to some good resources to learn Auto-layout principles. Thank you
If you can figure out your starting and ending layout constraints, then you can animate it using this template:
// add/remove constraints here or even change the constants of a current constraint
[view setNeedsUpdateConstraints];
[UIView animateWithDuration:0.25f animations:^{
[view layoutIfNeeded];
}];
Have a read of the Cocoa AutoLayout Guide
Also watch the WWDC videos on AutoLayout. WWDC 2012 232 Auto Layout by Example is a great one.
The easiest way will be to
define a constraint for the height of the image view;
define an IBOutlet for that constraint; and
when you tap on the button, set the constant for that imageview's height constraint to be 0, and then do the layoutIfNeeded as recommended by bandejapaisa:
[UIView animateWithDuration:0.25f animations:^{
[view layoutIfNeeded];
}];
If all of your other constraints are defined properly (e.g. the button's top constraint is to the image view and not the superview, the button has no bottom constraint to the superview, etc.), then this should yield the desired effect. Also, if you change the image view's contentMode to something like "aspect fill", then make sure you define the image view to clip subviews (so that you don't have the image spilling outside of the zero height image view).