Update constraints programmatically? - ios

I have a group of views in my view controller, each set with a constraint of 15 from the previous. When I press a button elsewhere on the VC, I have one of the views, near the top, double in height. How do I get the rest of the views to snap to their new constraints?

NSLayoutConstraint *sample = [NSLayoutConstraint constraintWithItem:yourTargetView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:yourTargetsParentView
attribute:NSLayoutAttributeLeft
multiplier:0.00
constant:100];
[yourTargetsParentView addConstraint:sample];

Related

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.

iOS: Autolayout differences with appliying constraints to subview on iOS 7 and iOS 8

I have a view controller set up in a storyboard. The view controller's view contains a subview(UITableView) with pinned edges to the 4 sides of its parent, essentially making the view fill its parent.
I am adding the view controller's main view as a subview of another view controller's view like this:
UIView *overlayView = firstViewController.view;
[overlayView setTranslatesAutoresizingMaskIntoConstraints:NO];
UIView *sourceView = secondViewController.view;
[sourceView addSubview:overlayView];
NSLayoutConstraint *constraint;
constraint = [NSLayoutConstraint constraintWithItem:overlayView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:sourceView attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0];
[sourceView addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:sourceView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:overlayView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:100];
[sourceView addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:overlayView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:sourceView attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[sourceView addConstraint:constraint];
constraint = [NSLayoutConstraint constraintWithItem:overlayView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:sourceView attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0];
[sourceView addConstraint:constraint];
[sourceView layoutIfNeeded];
I want to have a gap from the right edge of the view to its parent equal to 100 pixels.
Now weirdly enough this works as expected on iOS 8, but fails to do so on iOS 7 and the view is displayed full-screen ignoring the 100 constant set on the trailing constraint. Also, if the view controller's view that is being added has no child views - then it is working as expected. Is setTranslatesAutoresizingMaskIntoConstraints: being applied to all the subviews of a view in iOS 7 ? What might be the reason for this ?
EDIT:
The issue lies somewhere in the way subviews are being managed by the OS.
When the Container View is set as a outlet to the view property of the view controller, things don't work. If however, I set the Table View as an outlet to the view property, the it works. Something weird happens if there is a child view with pinned edges to its superview and then I am adding other constraints to the superview. I dont understand why it works fine on iOS 8 though...
EDIT 2
The problem seems to happen only with the trailing constraint. If I want to modify the constants of any of the other constraints there are no issues ?!
Can you try interchanging sourceView with overlayView in your constraint?
`constraint = [NSLayoutConstraint constraintWithItem:sourceView attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:overlayView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:100];`
This essentially means that sourceView.trailing - 100 = overlayView.trailing; which is not what you would want.
Edit 1:
Is setTranslatesAutoresizingMaskIntoConstraints: being applied to all the subviews of a view in iOS 7?
NO. translatesAutoresizingMaskIntoConstraints is set only for the views you explicitly call setTranslatesAutoresizingMaskIntoConstraints for.

NSLayoutConstraints with NSLayoutAttributeTrailing not as expected

I have an array with two new constraints.
When I set these constraints the button is placed 20 and 90 pix from the center of the superview.
I want the button 20px from the bottom and 90px from the right of the superview.
What am I doing wrong?
I create an array with two constraints:
NSLayoutConstraint *bottomTrailingConstraint = [NSLayoutConstraint constraintWithItem:_gameSettingsButton
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:_gameSettingsButton.superview
attribute:NSLayoutAttributeTrailing
multiplier:1.0f
constant:90.0f];
NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:_gameSettingsButton
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:_gameSettingsButton.superview
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:20.0f];
_bottomConstraintsArray = #[bottomTrailingConstraint, bottomSpaceConstraint];
I remove the old constraints and then add the new constraints:
NSArray *oldconstraints = _gameSettingsButton.constraints;
[_gameSettingsButton.superview removeConstraints: oldconstraints];
[_gameSettingsButton.superview addConstraints:_bottomConstraintsArray];
I also tried: [_gameSettingsButton.superview updateConstraints] but that changes nothing.
Any help would be greatly appreciated.
This:
NSLayoutConstraint *bottomTrailingConstraint = [NSLayoutConstraint constraintWithItem:_gameSettingsButton
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:_gameSettingsButton.superview
attribute:NSLayoutAttributeTrailing
multiplier:1.0f
constant:90.0f];
means:
_gameSettingsButton.trailing == _gameSettingsButton.superview.trailing * 1 + 90
That will make the button's trailing edge outside of the superview. Assuming left-to-right layout (so "trailing" means "right"), the button's right edge will be 90 points to the right of the superview.
Similarly, this:
NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:_gameSettingsButton
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:_gameSettingsButton.superview
attribute:NSLayoutAttributeBottom
multiplier:1.0f
constant:20.0f];
means:
_gameSettingsButton.bottom == _gameSettingsButton.superview.bottom * 1 + 20
Again, this will put the button outside of the superview. The button's bottom edge will be down 20 points from the bottom of the superview.
You either want to swap the first and second items or you want to negate the constant.
Also, you are querying the constraints from the button and then removing those constraints from the superview. Well, since the constraints you queried are not on the superview, that does nothing. (In fact, the result of your query is probably an empty array since nothing ever adds any constraints to the button itself.) You probably meant to query the constraints on the superview but I suspect there will be constraints unrelated to the button which you don't want to remove. So, you will need to remember the constraints you set up for the button (probably in an instance variable) and remove those. If the initial constraints were set up in IB, then you'll need to set up an outlet to track them.

How to set two different layouts for one view using constraints?

I will be short and very clear. I want to do what's on the figure below using constraints.
Any suggestions or solutions?
Description:
The coloured places are UIViews, containing for ex. 4 labels. So what constraint should I use to manipulate with the second UIView so in Portrait mode to be under the first one and in Landscape to be next to it?
The code below assumes you already have a reference to the orange view and the yellow view in your code. When in portrait mode you want them to be in sequence so you can have a layout as such
NSLayoutConstraint *portraitConstraint = [NSLayoutConstraint
constraintWithItem:orangeView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:yellowView
attribute:NSLayoutAttributeTop
multiplier:1.0f
constant:2.0f];
[self.view addConstraint:portraitConstraint];
When in landscape mode you can have a layout constraint as such
NSLayoutConstraint *landscapeConstraint = [NSLayoutConstraint
constraintWithItem:orangeView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:yellowView
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:2.0f];
[self.view addConstraint:landscapeConstraint];
Now these are not the full list of constraints you would need, if you build in the code and only the code you would have to have the orange view stick to the top, leading and trailing of the view and then in the code have the yellow view stick to the leading, trailing and bottom in the view for portrait.
In landscape you would have the orange view stick to the top, bottom and leading while the yellow view would stick to the top, bottom and trailing.
The constraints that are above would allow so you do not need a height to be set, but you might want to also say something like the orange view bottom is centerY - 1.0 in portrait and centerX - 1.0f in landscape thus avoiding the need for widths and heights and hence not worrying about the size of the screen. Center X and Y are below
NSLayoutConstraint *centerX = [NSLayoutConstraint
constraintWithItem:orangeView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f
constant:-1.0f];
[self.view addConstraint:centerX];
NSLayoutConstraint *centerY = [NSLayoutConstraint
constraintWithItem:orangeView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0f
constant:-1.0f];
[self.view addConstraint:centerY];
The above constraints should help you on your way to resolving the issue.

Resources