I'm creating a custom UIView called CTCycleClock with a subview called CTCycleSlider. It reacts to a gesture so it can rotate on one axis (like looking from above upon a roulette table).
To achieve this, the main view CTCycleClock creates two constraints on the CTCycleSlider subview that center it on X and Y.
Example:
Furthermore, the CTCycleSlider subview creates two constraints on itself that set a specific width and height. This is necessary because otherwise upon rotation, the disk will make itself larger.
This works nicely and correctly. But when the superview has a bigger size (for instance on iPad), I don't know how to tell AutoLayout that the subview has a new fixed width and height equal to the superview.
This is how I set constraints in the superview:
NSLayoutConstraint *centerX = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterX
multiplier:1.f constant:0.f];
NSLayoutConstraint *centerY = [NSLayoutConstraint
constraintWithItem:subview
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeCenterY
multiplier:1.f constant:0.f];
[self addConstraint:centerX];
[self addConstraint:centerY];
This is how I set constraints in the subview, where self.widthAndHeight is currently hardcoded to 320 on iPhone and 450 on iPad:
NSLayoutConstraint *w = [NSLayoutConstraint
constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:self.widthAndHeight];
NSLayoutConstraint *h = [NSLayoutConstraint
constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:self.widthAndHeight];
[self addConstraint:w];
[self addConstraint:h];
So my question is: how can I make a subview first hug the superview frame with a certain margin, but also set its width and height fixed?
EDIT: some clarifications as to why I need the constraint that sets width/height fixed.
When I won't set the width/height fixed, and the user touch-rotates the wheel, you get the following result:
In the above image, I've set constraints on the subview that set top/lead/width/height to the superview. That works great when the user hasn't rotated the wheel subview yet, but when they do, the autolayout constraints force the rectangular UIView smaller so it completely fits in the superview.
Thus the question remains: how can I create constraints that initially resize the subview correctly to the superview, but then set a fixed width/height so upon rotation, it stays the same size?
...how can I make a subview first hug the superview frame with a
certain margin, but also set its width and height fixed?
I don't understand your question. If you make your image view hug the superview with a fixed margin (on all sides) then the size of the image view is dictated by the superview.
You could pin the image view on 2 sides (e.g. top and left) and specify a size. Then the distance to the other 2 sides would vary based on the size of the superview. Or you could center it in the superview and fix the size, and then ALL The margins would vary based on the size of the superview.
Related
i have some problems sizing a detailCalloutAccessoryView that i added programmatically.
Here's the code for the view
HCSStarRatingView *annotationRating = [[HCSStarRatingView alloc] init];
annotationView.detailCalloutAccessoryView = annotationRating;
I tried to init the view with a initWithFrame but somehow that didn't work and i ended up with this.
I then discovered that i have to add NSLayoutConstraint programmatically to size the view correctly, so i added this code for constraints.
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:annotationRating attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:80];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:annotationRating attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:45];
[annotationRating addConstraint:width];
[annotationRating addConstraint:height];
And the view now looks like this
Now i want to get rid of the white space around it. I think i have to add a top and bottom constraint but i don't know how to do it because I don't know what items i have to relate to.
The excess whitespace is a function of the height that you've chosen. Your image is roughly 5 times as wide as it is tall, but you've asked to render it in a box that 80 x 45 pts (i.e. a view whose height is over half the width, rather than one fifth). If you pick the dimensions of the image view to match (adjusting for scale) the size of the image, you get something more like:
As you can see, with judicious selection of the width and height, there will be less whitespace than in your example. Note, there is some inherent whitespace between the detail accessory view that you cannot control, but by making sure you set the width and height correctly, you can reduce it to these minimal values.
I would like to constraint the center of subview to be at 1/6 of the overall width of the superview.
For example:
If superview's width = 6
CenterX of subview = 1
I wrote the following code in the superview class (self), to constraint the centerX of aSubview, and it is crashing:
// Hits here
[NSLayoutConstraint constraintWithItem:self.aSubview
attribute:NSLayoutAttributeCenterX
toItem:self
attribute:NSLayoutAttributeWidth
multiplier:1/6
constant:0];
// Crashes here
Is there a way to do this with NSLayoutConstraints at all?
I have two ideas why this might be crashing:
1) what is self in this context? Are you sure it's a UIView subclass?
2) 1/6 should result in 0, and that is not a valid multiplier. Try 1.0/6 instead
UPDATE:
method name is
+ constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
where's the relatedBy: part in your code?
UPDATE 2:
It seems it's not allowed after all. I tried to reproduce the crash and it logs the following error:
Invalid pairing of layout attributes
But! You can use Trailing instead of Width to achieve desired layout, it holds the same value actually, if the superview's left side is connected to the screen (see image to understand better).
This is tested and working:
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.aView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeTrailing
multiplier:1.0/2
constant:0];
[self.view addConstraint:constraint];
be sure to add your constraint to the superview of the view you want to position.
Create a spacer view. Set its hidden to YES. (Hidden views still participate in layout.) Pin the spacer's leading edge to the superview's leading edge. Constrain the spacer's width to be 1/6 of the superview's width.
Then constrain the “centered” view's center to the spacer view's trailing edge.
I have a custom UIView which uses autolayout programatically to set the size of the frame. For this purpose, I set a constraint on the width property of the view to be equal to that of the superview and then a constraint of the aspect ratio to be some hard coded value
//width constraint
NSLayoutConstraint *widhtConstraint=[NSLayoutConstraint constraintWithItem:entryView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:scrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:8.0f];
[scrollView addConstraint:widhtConstraint];
//aspect ratio constraint
NSLayoutConstraint *aspectRatioConstraint=[NSLayoutConstraint constraintWithItem:entryView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:entryView
attribute:NSLayoutAttributeHeight
multiplier:80.0/27.0//aspect ratio same as formula view
constant:0.0f];
[scrollView addConstraint:aspectRatioConstraint];
Please refer image:
I wish to change the aspect ratio of this frame on touch of a button(View More) by increasing its height and then later resize it back to original on touching the same button.Additionally how do I figure out the total height of the view governed by all its subviews such that each subview is visible without clipping.(Basically the standard collapse feature)
You can have the aspect ratio as a variable in your code and have the code to set the constraints in a method such as updateConstraints.
float aspectRatio; NSLayoutConstraint *aspectRatioConstraint=[NSLayoutConstraint constraintWithItem:entryView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:entryView
attribute:NSLayoutAttributeHeight
multiplier:self.aspectRatio//aspect ratio same as formula view
constant:0.0f];
Then when the button is pressed, in the action method you can modify the self.aspectRatio as fit and then call setNeedsUpdateConstraints and subsequently setNeedsLayout.
I haven't understood exactly what the question is, but changing constraints in response to a button press so as to expand / collapse a superview is easy:
If that is the kind of thing you are after, you can find a downloadable example project here: https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/bk2ch04p183animationAndAutolayout4
I have a UILabel which should be centered horizontally and the width should be set according to its content length. and on the left side of the UILabel an UIImage should be positioned which should be aligned to UILabel. if UILabel needs more space then it should push UIImage to the left, and if UILabel needs less space then it should pull UIImage toward x-center.
I had it without layout working fine, but has to use auto layout. I'm trying but i can't figured it out.
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-padding-[img(16)][lblUserName]-padding-|" options:0 metrics:#{#"padding":[NSNumber numberWithFloat:Padding]} views:displayViewDic];
is it possible with auto layout? so sometimes it will be like in number 1 and other times like number 2.
#"H:|-padding-[img(16)][lblUserName]-padding-|"
Here you're saying that the image has to be a fixed distance from the superview's leading edge. That doesn't match your description.
You might just need to change it to
#"H:|-(>=padding)-[img(16)][lblUserName]-(>=padding)-|"
To allow some flexibility in the margins.
To center a view horizontally, you have to manually create the constraint:
[view addConstraint:[NSLayoutConstraint constraintWithItem:lblUserName
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:view
attribute:NSLayoutAttributeCenterX
multiplier:1.0
constant:0]];
You don't need to use sizeToFit or any other methods like that - an image view and a label will have an intrinsic content size based on the image or the text.
Because you have an inequality, you may need to force the label to be as narrow as possible to prevent stretching:
[lblUserName setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
Add a horizontal center constraint to the label. Just this, and a suitable y position constraint would keep the label in the center. It'd expand equally in both directions to accommodate the content.
Now, add a horizontal spacing constraint to the image view's trailing space and the label's leading space for the x position, a suitable constraint for the y position (align vertical center with the label, perhaps?) and suitable constraints/image/intrinsic size for the size.
Code:
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[img(width)]-padding-[lblUserName]" options:0 metrics:#{#"width": 50, #"padding": 20} views:displayViewDic]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:lblUserName attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:superview attribute:NSLayoutAttributeCenterX multiplier:0.0 constant:0.0]];
In the interface builder we can pin height, pin width, make two views width equally, but how do I set the constraints so that when a view is being resized, it maintains its width/height ratio?
In my particular case, I have an UIImageView in my view controller. When the view resizes, I'd like my image view to resize, but maintain a 3:2 width:height ratio. Is it possible to do it in IB? Is it possible to do it with code?
Thanks!
You can add an aspect ratio constraint in IB by control dragging from the view to itself and choosing aspect ratio.
I don't think you can do that in IB, but in code, it can be done like this (iv is my outlet to the image view):
[self.iv removeConstraints:self.iv.constraints];
NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self.iv attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:100];
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:self.iv attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.iv attribute:NSLayoutAttributeWidth multiplier:.66 constant:0];
[self.iv addConstraints:#[con1,con2]];
This explicitly sets the height to 100, and the width to height ratio to 3:2.
This guide from Apple really helped me - it outlines two approaches, one where you turn off AutoLayout for certain subviews and set frames directly, and another where you use pure AutoLayout via code.
https://developer.apple.com/library/ios/technotes/tn2154/_index.html