Working with constraints in storyboard and code - ios

I have a button and a view, when the button is not appearing I want the view to be extended (using top constraint) to fill up the white space, the button appears I want the view to shrink to make room for the button. The problem is I am getting this error:
failed: caught "NSInvalidArgumentException", "NSLayoutConstraint for (null): Constraint must contain a first layout item"
I set constraints in storyboard, and declare the top constraint by this:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *containerTopConstraint;
This is how I change the value of the constraints:
[_containerTopConstraint setConstant:20];
How can I fix this error?

I'd try putting your views inside of a UIStackView. It automatically repositions views when the isHidden property changes on one of them to take up the empty space.

Related

Give a dynamic value to trailing space of a UIView in StoryBoard

I have two UIViews with parent and child relation to each other. I want to set a trailing space of childView to its parentView with unknown value. When i set the margin with constant value, it always place with that value but i need to place that with the value according to its width. How can i set constraint in StoryBoard? I can't set that again in program because i have more child views beside.
There are 2 Approaches of achieving this..
1: create an object of your desired constraint using IBOutlet and change the value in the ViewController.m file.
for example..
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *trailingSpaceConstraint;
Now change the constant value to any desired number. In my case i want to set the trailing space to 3 times 1/4th of the screen width.
self.trailingSpaceConstraint.constant = self.view.frame.size.height/4*3;
2: Select any constraint and navigate to property attribute and change the value according to Add Side Class Customization by clicking the + sign left to the constant field in that property attribute section of any constraint.

Autolayout issues when adding a new view into a hierarchy

I'm struggling big time with a couple of scenarios when i have a UIView with constraints applied via IB and adding a new UIView into it's view hierarchy. Here's how it goes:
I have a full screen UITableView with the following constraints applied so it scales nicely through all the resolutions of iOS devices:
Now i'd like to add a UIView above the UITableView acting as a sort-of toolbar.
Right now the only possible way i succeeded in adding this view is adding it on top of the TableView, so that it covers the top of tableview. What i'd like to achieve is the newly added UIView pushing table down so both are visible. I tried several things including adding a container view in IB just above the tableview however that didn't work at all.
So my question is: is there a way to dynamically edit, remove and add new constraints to the view hierarchy, ideally supporting animation?
You can create IBOutlets for a constraint, just like any view.
Each NSLayoutConstraint object has a constant property that you can set anytime in code (it's the value of the constraint).
So you would create both views on Interface Builder, and constraint the top of the table view to the bottom of the new view.
The new view will have a set height constraint, and you create an outlet to that height constraint to make that view appear or disappear in code.
There are other possible solutions but I think this one is the easiest.
The code would be something like this:
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *topHeightConstraint;
To set the value:
self.topHeightConstraint.constant = 60;
and to animate it:
[UIView animateWithDuration:0.6 animations:^{
self.topHeightConstraint.constant = 60;
[self.view layoutIfNeeded];
}];

How to remove space of hidden UIImage?

I'm stuck that I don't know how to remove spacing of hidden UIImage. Purpose is If flag is true, UIImage will be displayed and if flag is false, UIImage is hidden but space of this hidden image is still there.
I'm using Auto Layout.
If you are using Auto Layout and Storyboard you can set create an IBOutlet as a property of your Controller class. You then hook that up to the width constraint of the UIImage. In your code when the UIImage is hidden you set the IBConstraint to 0. When it is shown, you set the IBConstraint back to the normal width.
(in Controller.h)
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *constraintImageWidth;
(in Controller.m) (pseudo code)
if(hidden){
constraintImageWidth.constant = 0;
}
else{
constraintImageWidth.constant = 30;
}
There should also be a horizontal constraint between the left side of the label and the right side of the image set up in the Storyboard.
Here is where you would check the width box to add the width constraint.
Here is where you would connect the referencing Outlet to the IBOutlet on your controller
The easiest and most effective way to handle this is using Stack Views.
Insert both the images in a horizontal stack view and stack view will internally take care of the spacing. Additional properties like alignment, spacing can be tweaked as per requirement.
Keep in mind that you will have to re-establish the constraints between stack view and adjacent elements since once the views are added to a stack view all if its constraints are cleared

Error using activateConstraints on a collection of Container View constraints

In my iOS 8 app using size classes, to get the iPad to have a different layout in portrait and landscape I have an IBOutletCollection for each orientation that I activate and deactivate. This has worked great up until the introduction of a Container View.
I'm adding a new VC that is a separate tab item in the iPhone version, but I embed it into the iPad layout using a Container View. I'll focus on just one size class, RegularRegular. The container view has 4 constraints: Trailing, Leading, Top, and Bottom. When I run the app, it looks fine (in that single orientation).
Now, when I add these 4 constraints to my IBOutletCollection, the activateConstraints message fails with the following error message:
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with items ; layer = ; contentOffset: {0, 0}; contentSize: {460, 78}> and > because they have no common ancestor. Does the constraint reference items in different view hierarchies? That's illegal.'
The constraints don't reference items in different view hierarchies. Top, Bottom, and Leading are all in relation to another view that is also in the VC's view. The Trailing is in relation to a stepper that is also in the VC's view. So they're all at the same hierarchal level to my knowledge.
When in debug, I can see that the constraint starts off with its active property as nil. This appears to be normal (the non-container constraints started off the same prior to them being set to YES). I checked this with a for loop setting the active property.
Is there something I'm doing wrong with the constraints on the Container View? Are constraints on a Container View somehow special because they are on a Container View? I've tried finding the answer myself, but I can't seem to find anything in regard to this issue I'm having.
Any help would be greatly appreciated.
4/10 Edit to provide pictures & code. Note at this time I've broken out the container constraints into a separate array so I can deal with them on their own.
In the image below, the container's Leading, Top, and Bottom constraints are in relation to the view to the right of the container. The Trailing constraint is in relation to the stepper above it.
Related code:
#property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *regularAnyConstraints;
#property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *regularAnyContainerConstraints;
#property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *anyRegularConstraints;
#property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *anyRegularContainerConstraints;
These are the constraints in the _anyRegularContainerConstraints array:
[NSLayoutConstraint deactivateConstraints:_regularAnyConstraints];
[NSLayoutConstraint activateConstraints:_anyRegularConstraints];
[NSLayoutConstraint deactivateConstraints:_anyRegularContainerConstraints];
[NSLayoutConstraint activateConstraints:_anyRegularContainerConstraints];
When in debug, I can see that the constraint starts off with its active property as nil
That remark makes me wonder whether you might be misinterpreting what activateConstraints and deactivateConstraints do. Despite their names, and despite the misleading entry in the documentation, they actually add and remove constraints. Thus, instead of confusing yourself with these commands, I would suggest you might do better to call addConstraints and removeConstraints directly. The advantage of doing this is:
These latter are UIView methods, so you will be in conscious control of what view the constraints are added to.
When removing, you will be clear on your responsibility to retain the removed constraints if you want to use them again later.
I realize that this doesn't answer your question directly, but you didn't provide enough info for that (you didn't show your code that calls activateConstraints and deactivateConstraints, you didn't show any diagram of the view controller and view hierarchies, etc.) so it is not entirely clear where you are going wrong. Instead, I'm trying to get you to reorient your thinking as you try to work out the details of the problem.
The issue was that the container view was not 'installed' at the time the constraints were being applied. I am de/activating out of viewWillLayoutSubviews so at that time the size class must not yet be determined.
To resolve this I now have the Container View installed for AnyAny. Then I deselected all of the size classes that I don't want it present for.
I eventually discovered this was the issue because my Container View's UIView was not in self.view.subviews.

UITableView Auto-Layout Flexible Height

I have following design.
Am using AutoLayout to make every thing flexible. In this design i have a UIView which is Gray in Color as showing in image and a UITableView below UIView. Some times i have to show this UIView and some times i have to hide this Gray UIView.
My problem is when ever i hide UIView, UITableView is not fixing its height. I don't want to hard code in .m file. Is it possible AutoLayout take care of this issue. I have these constraints as below image. Am i missing any constrain.
When i try to change UIView height, UITableView is not moving up and showing some orange constraints as show in image.
The contraints looks good. All you have to do to show/hide the gray UIView is change the height constraint constant.
To do this, create an IBOutlet in your controller for the constraint (you do this the same way you would for a UIView IBOutlet), and when you want to hide the gray view, set the constant property of the constraint to 0.
eg.
#interface MyViewController
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *greyViewHeightConstraint;
#end
and when you want to hide the view:
self.greyViewHeightConstraint.constant = 0;
To show the view again, you would have to save the "default" constant value after the storyboard is loaded (like in viewDidLoad for example), and set self.greyViewHeightConstraint.constant to this saved value.
Note also that these constraint changes can be animated.
The "orange constraint" you are seeing in Interface Builder is normal: it indicates that the view frame is not matching the constraints you set. You can then update the frame to respect the constraints, or update the constraints to match the frame.

Resources