Adding constraints without auto-layout - ios

I am building an app with a modal view containing a UICollectionView and below it a view containing two buttons (validate/cancel).
The number of rows in my UICollectionView can change depending on the data and don't know it beforehand so I want to add a constraint to always keep my buttons 30px below the collectionView.
I am not using auto-layout for this as I have some animations which work better without it so I don't know how to programmatically add such constraints.
Does anyone have any idea how to do it?
Many thanks for your help

I am not sure about the collection view. However, the below works with a standard view and since UICollectionView is a type of UIView, then the code might work with collection view also
NSLayoutConstraint *bottomConstraint=[NSLayoutConstraint constraintWithItem:buttonA attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeBottom multiplier:0.45 constant:0];
NSLayoutConstraint *widthConstraint=[NSLayoutConstraint constraintWithItem:buttonA attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:95];
NSLayoutConstraint *heightConstraint=[NSLayoutConstraint constraintWithItem:buttonA attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:95];
NSLayoutConstraint *leftConstraint=[NSLayoutConstraint constraintWithItem:buttonA attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeLeft multiplier:1 constant:20];
[self.view addConstraints:#[bottomConstraint,widthConstraint,heightConstraint,leftConstraint]];
The above basically lays out the buttonA relative to the view. However, to use this, the auto layout should be off (as in your case).
The bottom Constraint says that this constraint is related to button A and will act on its attribute NSLayoutAttributeBottom(bottom side) and the bottom side will be present on exactly 0.45*(Bottom of view).
The width constraint specifies that it is related to button A and will act on its width. The width is not dependent on any other object (toItem is nil) and it will have a constant value of 95.

Related

How to add subview with half-transparent background which won't break and won't be affected by AutoLayout?

I want to show user that application is loading data using some progress bar and label on top of view which will be shown after the data is loaded. This view is not initial, so I cannot use LaunchScreen for these purposes. What's the best way to do so?
The view which will be shown after uses AutoLayout, and it'd better be that view on top uses AutoLayout as well, but those AutoLayouts shouldn't interact with each other in any way.
First create a custom UIView class, and customize the view according to your requirement. i.e. transparent background, add image/label etc. using autolayout/with the help of code.
Now add this custom view (yourSubView) into its containerview and add some constraint so that it place in right location.
The following code will add a subview after keep space 80px in all side (left, right, top, bottom), you can add constraint according to your requirement.
[yourContainerView addSubview:yourSubView];
yourSubView.translatesAutoresizingMaskIntoConstraints =
NO;
[yourContainerView addConstraint:[NSLayoutConstraint constraintWithItem:yourSubView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:yourContainerView attribute:NSLayoutAttributeLeft multiplier:1.0 constant:80.0]];
[yourContainerView addConstraint:[NSLayoutConstraint constraintWithItem:yourSubView attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:yourContainerView attribute:NSLayoutAttributeRight multiplier:1.0 constant:80.0]];
[yourContainerView addConstraint:[NSLayoutConstraint constraintWithItem:yourSubView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:yourContainerView attribute:NSLayoutAttributeTop multiplier:1.0 constant:80.0]];
[yourContainerView addConstraint:[NSLayoutConstraint constraintWithItem:yourSubView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:yourContainerView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:80.0]];
What about using something like https://github.com/sgryschuk/SGNavigationProgress?

iOS - replicating a constraint programmatically

I created this constraint in Interface Builder. Without it, the below textview expands upwards as its content grows, with it, the textview expands downwards as its content grows.
How do I create that constraint in programmatically?
Here is what I tried:
[self addConstraint:[NSLayoutConstraint
constraintWithItem:_textView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_internalScrollView //this is the parent view
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:0.0]];
but it has no material affect on anything.
The UITextView object I am using is from this library https://github.com/MatejBalantic/MBAutoGrowingTextView but that is a red herring to this question.
Here is what you need to do.
[_internalScrollView addConstraint:[NSLayoutConstraint
constraintWithItem:_textView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_internalScrollView //this is the parent view
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:300.0]]; // constant should be 300 as shown by you in screen shot
btw, the above screen shot shows you are making constraint with top layout guide and not with parent view of textView if that is the case then layout attributes should be changed in the above code according to your needs

Autolayout and UIScrollview and Child View Controller issues

In my app I have an embedded child view controller into which at runtime I have to add one or more "Editor UIView"s based on data coming in from the server. I could model each "Editor" as a complete UIViewController in a xib file and add it at run time. The parent of the "Editor" has some items of its own (like titles and a couple of buttons) so I've tried to build a layered approach using a single child UIViewController and loading the "Editors" into its UIScrollview on the fly. However I can't get Autolayout to cooperate. This diagram shows the basic arrangement.
The problems start with just having the "Content View" embedded in the UIScrollview. I can get the content view to work OK with the scrollview. When I add the editor view and add runtime constraints to the content view, I get multiple auto layout complaints plus the scroll view content size is 0,0 so I am clearly not getting what I need.
Any ideas on how to approach this? I could always simply duplicate the child controller's extra items for each editor but it would be nice to have this work in this layered fashion.
Note I did this first with a UITableViewController where each editor is a UITableViewCell and that auto layout liked but I wanted to see if I could do it without the table. Maybe I will go back to that.
Added these at runtime:
_editor = [self editorForDataType];
[_contentView addSubview:_editor];
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:_editor
attribute:NSLayoutAttributeLeading
relatedBy:0
toItem:self.contentView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:0];
[self.contentView addConstraint:leftConstraint];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:_editor
attribute:NSLayoutAttributeTrailing
relatedBy:0
toItem:self.contentView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:0];
[self.contentView addConstraint:rightConstraint];
NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:_editor
attribute:NSLayoutAttributeLeading
relatedBy:0
toItem:self.contentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:0];
[self.contentView addConstraint:topConstraint];
NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:_editor
attribute:NSLayoutAttributeTrailing
relatedBy:0
toItem:self.contentView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
[self.contentView addConstraint:bottomConstraint];
In the end the key to what I needed was the follow set of constraints, plus adding the constraints for the editor view in viewDidLayoutSubviews and saving a property for the content view Height.
In viewDidLoad I set the height property to the height of the editor view. Now it works great.
Your last two constraints are incorrect. You have leading to top and trailing to bottom, those should be top to top and bottom to bottom. However, those constraints probably aren't what you want. If it's the first editor view, you probably want the top constraint to the content view and a height constraint. For any subsequent ones, you should pin the top to the editor view above it, rather than the content view.

Auto Layout Constraint With Dynamic Variable?

Is it possible to have an Auto Layout constraint (NSLayoutConstraint) with a dynamic constant/multiplier?
For example, this would be a bog standard NSLayoutConstraint:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:obj1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:obj2 attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
Then here would be an alteration of that constraint but a dynamic variable in the constant:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:obj1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:obj2 attribute:NSLayoutAttributeLeft multiplier:1 constant:scrollView.contentOffset.x];
The second one would take the contentOffset of the scrollview and use it as the constant. However, having tried this, it only uses the offset which exists when the constraint is made.
What I would want it to have the constraint update the constant, when the scrollview if scrolling. This way it would keep using the most up to date contentOffset.
Is this possible?
Yes definitely.
In fact, that is what they are built for. When you are animating views etc... you need to be able to change the constraints.
Counter-intuitively the only property of NSLayoutConstraint that is writable is the constant property. (lol)
You are not quite right in your code though.
Creating the constraint you would first create a property for it...
#property (nonatomic, strong) NSLayoutConstraint *leftConstraint;
Then create it...
self.leftConstraint = [NSLayoutConstraint constraintWithItem:obj1 attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:obj2 attribute:NSLayoutAttributeLeft multiplier:1 constant:0];
Then edit the already existing constraint...
self.leftConstraint.constant = scrollView.contentOffset.x;
Then you need to force the view to relayout the subviews...
[self.view layoutIfNeeded];

iOS Auto Layout issue

I'm trying to make constraints programmatically. I have textField and button created in IB. Here is the code:
UIView *superview = self.view;
self.button.translatesAutoresizingMaskIntoConstraints = NO;
self.textField.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.textField
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:0];
[superview addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:self.textField
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:0];
[superview addConstraint:constraint];
constraint =[NSLayoutConstraint constraintWithItem:self.button
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.textField
attribute:NSLayoutAttributeLeading
multiplier:1
constant:-10];
[superview addConstraint:constraint];
constraint =[NSLayoutConstraint constraintWithItem:self.button
attribute:NSLayoutAttributeBaseline
relatedBy:NSLayoutRelationEqual
toItem:self.textField
attribute:NSLayoutAttributeBaseline
multiplier:1
constant:0];
[superview addConstraint:constraint];
And after running I have this issue:
Constraints also have no effect on views.
What am I doing wrong?
First in the storyboard editor select the view controller you want to change then, in the constraint editor, select "ADD missing constraints in view controller"
Select an elment that you'd like to modify at runtime by remomiving and adding new constraint, in the object inspector panel select each constraints and edit it by checking the box "placeholder: remove at build time".
This is a way to say to storyboard editor that you are fine with the constraint ant it should not apply or complain about an insufficient constraints situation.
Constraints flagged are removed at runtime, so you should provide you own constraint and is better if you do in the right place. Ovverride -updateViewConstraints in the view controller and remember to call super and add you new constraints.
You have three auto-generated constraints "IB auto generated at build time for view with fixed frame". These conflict with the ones you make yourself. Probably, you have set fixed size properties on the text field and button in Interface Builder. Possibly, you have a fixed width on your button, and that conflicts with the trailing constraint.
Combining IB with code constraints can be tricky. I have found it is often easier to design things only in IB or only in code to avoid conflicts like these, although combining the two is perfectly viable.
For a good explanation on what NSIBPrototypingLayoutConstraint is, have a look at this SO answer:
Trouble with AutoLayout on UITableViewCell

Resources