Autolayout - Match height, not heights (Unidirectional) - ios

Is it possible to have a unidirectional size match using autolayout and interface builder?
For example, I might have two labels. I don't want label A to be larger than label B, and I want B to have its intrinsic size. But using "match heights/widths" could result in a large amount of text increasing A's size, and therefore B's.

The way to do this would be to have two constraints.
An equal heights constraint between the label and the image view.
A height constraint on the image view.
This will first set the height of the image view with the fixed height constraint and then will set the height of the label from the height of the image view (equal heights).
By doing this the label will not grow with the amount of text it has. Its height is effectively fixed by the image view.
It will not make the image view get any bigger as this would contradict the fixed height.
EDIT FOR NEW QUESTION
OK, for this you would do pretty much the same thing. It might be a bit tricky in interface builder though as I'm never sure which is item1 and item2 in the constraint when done through IB.
You could do it very easily though by adding one line of code...
[theSuperview addConstraint:[NSLayoutConstraint constraintWithItem:labelA
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:labelB
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0.0]];
This is exactly what the interface builder constraint does but I'm not sure if you can tell which way around the item1 and item2 are.
This is your "unidirectional" equal heights attribute though.
EDIT 2
There may or may not be an update some time in the future that might let you see item1 and item2 in interface builder.

Related

Changing Constraints Programmatically

I have a user form where there are a bunch of Label and its corresponding text field or drop down or checkbox group arranged vertically on Storyboard. Something like this:
Label 1
Text Field 1
Label 2
Check Box Group
Label 3
Drop Down Field
Label 4
Text Field 4
Under normal condition, constraint set up between Labels is 60 vertically.
The problem is since Check Box Group is dynamically populated from the data retrieved from server, I don't know how many checkboxes will be there between Label 2 and Label 3 at StoryBoard design time. So there comes a need to programmatically change the constraint between Label 2 and Label 3.
Currently, what I did was set constraint 60 between them but check "Remove at build time" and programmatically set the constraint depending on how many checkboxes are there between there. Something like this:
[_superView addConstraint:[NSLayoutConstraint constraintWithItem:_Label3 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_Label2 attribute:NSLayoutAttributeBottom multiplier:1 constant:100]];
But for some reasons , this is not working as expected. Although Label3 is correctly placed after the last checkbox, I could only see half of the Label4 which has height constraint. I tried:
[superView updateConstraints];
[superView layoutIfNeeded];
But to no avail.
You can add vertical spacing constraint in storyboard, and add an nslayoutconstraint iboutlet var in view controller and connect the same to the constraint in storyboard.
Now the constraint.constant value can be changed programmatically to whatever value you want to instead of adding it.
Personally, I'd be inclined to not have any constraints between "Label 1", "Label 2", etc. I would instead create a view with container views that will contain the various fields between the labels:
I'd define vertical constraints like so (I'm assuming you'd do this in storyboard, like I did above, but I'm showing example VFL as that's a concise and precise way of describing a whole set of vertical constraints):
V:|-[label1]-[group1(0#250)]-[label2]-[group2(0#250)]-[label3]-[group3(0#250)]-[label4]-[group4(0#250)]-|"
Note, in my snapshot, I gave those container "group" views a non-zero height and a gray background, so you can see them, but hopefully this illustrates the idea. Have placeholder views that will contain the controls underneath the various labels and give that a zero height but a low (250) priority (or, like you did, have it omit them at run time).
Then you can programmatically add whatever controls you want as subviews of their respective group views as set their constraints accordingly, e.g.
V:|-[MoView]-[LarryView]-[CurlyView]-|
And because those have a higher prior than the zero height constraints of the group boxes, the resulting view will be resized accordingly, e.g.:
You can probably accomplish something very similar using UIStackView (if targeting iOS 9 and later), but if using simple constraints, this is how I'd tackle it.
That way, you get out of the business of coding magical spaces between "Label 1" and "Label 2", which will become invalid if, for example, you change fonts at some future date (or better, support the use of the user-provided font sizes specified within the Settings app).

How to dynamically change the height & width of view in iOS?

I am developing an app in iOS.I have used autolayout ,constraints to build the UI.But still i am very much confused about it.I am not able to create accurate UI using autolaout & size classes.Please pardon me if i am asking very basic question because i am very fresh to iOS.
Here I have created a Login screen.In which i have a container in middile which contains some text filed & image at top which is kind of app logo and below the container i have two buttons.I have three problems.
Container is not aligned properly.It size keep on increasing & margin between container & text fields is increasing.So UI looks bad.I want to increase width & height in equal proportion so that it does not look bad.
2.The size of logo should be increase in equal proportion.It's height & width.
3.Buttons at bottom their height is fixed.Their height should increase in equal proportion.
ScreenShot
Hi if you want keep proportion you need to work with aspect ratio constraints as such:
NSLayoutConstraint *constraint =[NSLayoutConstraint
constraintWithItem:view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:childView
attribute:NSLayoutAttributeWidth
multiplier:4.0/3.0 //Aspect ratio: 4*height = 3*width
constant:0.0f];
[view addConstraint:constraint];
One more thing you need to know is not to limit the view grow with other constraint.
Lets say you want the top image to grow don't add some fix leading or trailing from the side, just add top constraint and center the view to its superview.
If you want to limit the image size so it want get out of superview bounds just add a constraint leading and trailing with gratherOrEquelTo relation
Hope I was clear.
//Edit
If you want some minimum size for height or width just add a constraint of height or with the minimum size you want with the relation of gratherOrEquelTo

autolayout storyboard constraints right aligned based on subview content

I am trying to set up the following layout (all vertical centred so I'm ignoring that aspect):
| Label btn View |
|-10-|-----X-----|-10-|--30--|-10-|---Y---|-10-|
A B C D E F G H
A - left edge of superview
B - left hand edge of UILabel
C - right hand edge of UILabel
D - left hand edge of Button
E - right hand edge of Button
F - left hand edge of UIView [view]
G - right hand edge of UIView
H - right edge of superview
The button width is fixed (the 30). Depending on some other factors I add a different sub-views to [view] at run time. The different sub-views have different widths.
I would like Y to scale with the width of the added subview (and preferably respond to changes accordingly).
I would like X to take the remainder of the space, thus making the whole set fill the width of the superview.
~
I am trying to work out how to do this with the storyboard tools (manipulating constraints and frames at runtime would make this a different problem, and an easier one for me).
I currently have spacings set up as:
Label to LHS: 10, priority 1000
Label to Button: 10, priority 1000
Button to View: 10, priority 1000
View to RHS: 10, priority 1000
The subview has a layout like:
|-10-|-fixed width button-|-10-|-fixed width button-|- (no constraint) -|
And also tried:
|-10-|-fixed width button-|-10-|-fixed width button-|-10-|
(Which resulted in a correct size for subview, but [view] just takes up whatever space is left after hugging the UILabel content size.)
I have tried changing the content-hugging and resistance properties of the view (thinking this was the key), but the only outcomes I can get to happen are:
Fixed width View (ignores contained content)
Label shrinks to content size
Specifically, I made the content hugging and compression property of View greater than that of label, thinking that this would cause it to hug the content, but I ended up with the Label shrinking to the width of the content and the View taking up the remainder. I have read this Cocoa Autolayout: content hugging vs content compression resistance priority but either misunderstood it and/or have to consider something else in my solution.
In case it makes a difference, the Label runs onto two lines (sometimes).
With some hints from #rdelmar (thanks!). I managed to find a solution that I'm reasonably happy with.
Basically, it looked like I'm not able to do this in the storyboard, that the subview won't dictate the superview as it is added through code.
So I needed to add some constraints, and crucially (to avoid it conflicting with the subview's original understanding of things:
[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
NSMutableArray * constraints = [NSMutableArray array];
[constraints addObject:[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeLeftMargin
multiplier:1.0
constant:0]];
[constraints addObject:[NSLayoutConstraint constraintWithItem:subview
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeRightMargin
multiplier:1.0
constant:0]];
[view addConstraints:constraints];
If someone is able to explain this answer, or indeed improve (e.g. storyboard only) then I think the world would be a better place.

NSLayoutConstraint Positioning proportionally

The parent view (green rectangle) contains many subviews.
Lets consider only for one subview it has horizontal position with x offset from 0.
And whenever the parent view's width is changed lets say with factor of a, I need the subview to be positioned with offset equal to a * x
As much I understood NSLayoutConstraint does not allow to set a contraint for red subview's attribute NSLayoutAttributeLeft based on containing green views width NSLayoutAttributeWidth.
Any suggestion/reference how to acheive proportional positioning like described by means of NSLayoutConstraint is welcomed.
You won't be able to lay it out in Interface Builder but you can use the NSLayoutAttributeLeft on the super view in code.
This way you can give it a multiple which will keep the position correct when the superview is updated.
If that doesn't work you can use a "spacer" view.
So you will have them both in the green superview like this...
|[spacerView][redView] //using VFL
Then you can set the width of the spacer view proportionally to the width of the green view.
Just set the spacerView to hidden or give it an alpha of 0.0 so it doesn't show.
As you said, you cannot relate the left attribute of your subview with the width of the superview, but you can relate it to NSLayoutAttributeRight, which has the same value as the width. So you should be able to do it by using a multiplier (and 0 for the constant),
[_greenView addConstraint:[NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual toItem:_greenView attribute:NSLayoutAttributeRight multiplier:.1 constant:0]];
Of course you need to do the calculation to figure out, based on the width of the green view, what the multiplier should be. So, in my example, the greenView was 200 points wide to start with, and I wanted the red view to be 20 points from the left, so I used 0.1 for the multiplier.

"Width equals height" constraint in Interface Builder

I can't find a way to create a 'square' constraint, meaning 'width equals height' in Interface Builder. I guess it's possible to add such constraint programmatically. Is there something I can do in IB? Maybe I just don't see it? It seems trivial, yet I can't find it.
Update Xcode 5.1b5
Ctrl+click and drag from a view and release while the pointer is over the view. Select "Aspect Ratio". It will create a constraint where the first and second item is the view.
Before Xcode 5.1
You can't because the width/height editor lacks the fields to relate to another property or set the ratio:
Therefore, you can't express the following code in Interface Builder:
CGFloat ratio = 1.0;
NSLayoutConstraint *constraint = [NSLayoutConstraint
constraintWithItem:myView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:myView
attribute:NSLayoutAttributeHeight
multiplier:ratio
constant:0];
constraint.priority = 1000;
[myView.superview addConstraint:constraint];
Please add a new constraint, aspect ratio to 1:1 on the UI element as in the image.
To start, control drag diagonally from the button to itself. A contextual menu will appear, where you can add width and height constraints. Shift+Click on each; a checkmark will appear indicating that you have added the constraint. (If you accidentally dismiss the dialog before adding both, that’s OK, just repeat the drag step and set the other one):
When first added, these constraints take on the current width and height of the button, so you’ll need to adjust each constraint to give it a more appropriate value. We’ll have to do this one at a time, although our image is square, so be sure to use the same constant value in both constraints to resize the button proportionally. Double-click on the constraint, and enter a smaller value in its constant field:

Resources