autolayout storyboard constraints right aligned based on subview content - ios

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.

Related

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

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.

Autolayout - Match height, not heights (Unidirectional)

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.

Proportional distance constraints in auto layout

I am not able to achieve the desired layout of the views with the auto layout in >iOS6.
I have UIView1 and UIView3 which are fixed to the parent view (correspondingly with the TopSpaceToSuperview, BottomSpaceToSuperView and FixedHeight) and they behave as expected when the parent view changes the height.
Which constraints should I specify in the IB for the UIView2 if I want it to maintain the same proportional distance to its siblings (UIView1 and UIView3) when the parent view changes the height? (as displayed in the image)
The way to do this is to use invisible "spacer" views between your views.
You can't have relatively sized spaces so use these views instead.
Where the current spaces are place a UIView in each.
Then (in code as you can't do this in IB) set a height constraint between these with the correct multiplier that you want.
i.e.
[NSLayoutConstraint constraintWithItem1:spacer2
attribute:NSLayoutAttributeHeight
relation:NSLayoutRelationEqual
item2:spacer1
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0];
Then make the other views "stick" above and below these spacer views with 0 spacing.
Then you just have to hide these views and the auto layout will take care of the rest.

Autolayout - Position by Percentage

I'd like to map dots along a line.
The line length depends on device and orientation and is always stretched across the whole screen. Therfore it would make most sense to me to position the dots using a relative percentage value.
So far I only found constraints being defined in some sort of point value (Doc)
Is it possible to use percentage values as constraints as well? Any ideas on how to position these dots in a scalable way ... or do I need do this conversion/positioning "manually"?
Yes, you can position the dot as a fraction of the view's width. The NSLayoutConstraint method, constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:, has that multiplier parameter that lets you use a fractional relationship between a superview and its subview. The superview's right edge will be the width of that view (the screen if its a full width view), so if you create a constraint like below, the dot will be positioned at a fractional distance along the line:
-(void)viewDidLoad {
[super viewDidLoad];
[self.view removeConstraint:self.leftConDark];
[self.view removeConstraint:self.leftConLight];
NSLayoutConstraint *lcd = [NSLayoutConstraint constraintWithItem:self.darkButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:.5
constant:0];
NSLayoutConstraint *lcl = [NSLayoutConstraint constraintWithItem:self.lightButton
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeRight
multiplier:.9
constant:0];
[self.view addConstraints:#[lcd,lcl]];
}
In this example I'm positioning two UIButtons (info type dark and light). I added them in IB, and made IBOutlets to their constraints they have to the left side of the view (that's what the system gave me, it could have been to the right side -- it doesn't matter since you just delete them anyway. If you are making the dots in code, you wouldn't need to do this). In code I remove those constraints, then add new ones that will put the center of the buttons at 50% and 90% of the way across the screen.
I'm not an auto-layout expert (or even competent yet!) but as of XCode 6, you seem to be able to do this in IB.
Select the object.
Show the size inspector (click on ruler)
Double-click on the constraint rectangle (not obvious! "Edit" takes you somewhere else)
Here you can set the multiplier just like in the answer above.

Resources