I am trying to programatically setup some constraints. I have one container view UIView which holds three subviews.
UIView - circleView
UILabel - label1
UILabel - label2
The circleview is shown at the top of the container at (0,0,width,80). The label1 is shown underneath the circleview with 5.0 padding.
I am now trying to add the label2 to be in the center of the circleView. How do I do this with AutoLayout programatically.
This is what I currently do.
NSDictionary *views = NSDictionaryOfVariableBindings(circleView,labelView, iconLbl);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[circleView(circleSize)]|" options:0 metrics:metrics views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[labelView]|" options:0 metrics:metrics views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[circleView(circleSize)]-(padding)-[labelView]-|" options:0 metrics:metrics views:views]];
The label2 is the iconLbl view in the dictionary.
This should be relatively straightforward - it helps to use xib to see how many constraints you actually need to get the effect you want. Constraining a label to be in the center of another view, both of which are in a parentView, only requires 2 constraints to be fully constrained. If this were a regular UIView, you'd need 4 constraints (x,y,width,height), but the label will automatically determine it's width and height from it's content, so it's not ambiguous. This is of course if you other views are all properly constrained, but you only asked about the label2 in the circle view.
I prefer to use the non-visual form for defining constraints because they read like mathematical equations. What you want is:
label2.centerX = circleView.centerX*1 + 0;
label2.centerY = circleView.centerY*1 + 0;
Since these are siblings with a common parent, the constraints are added to the parentView. So you get the following two constraints.
[parentView addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:circleView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[parentView addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:circleView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
This is sufficient for getting label2 centered in the parentView. Any issues you get will likely be due to other constraints between your views not being properly specified.
Can you try this?
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[circleView(circleSize)]" options:0 metrics:metrics views:views]]; //Dont link to both the sides. Dock to the left edge
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self. labelView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0 ]]; //Specify the X
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self. labelView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0 ]]; //Specify Y
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[circleView(circleSize)]" options:0 metrics:metrics views:views]]; //Dock the circle to the top
With Masonry library
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.center.equalTo(view);
}];
Related
I am trying to add a UIlabel to a UIView class.
it should be in the following format -15-Label(stretch to max width)-15.
Top spacing=15 and height fixed to 30.
Two issues with the following code:-
1) Label does not stretch to max width
2) Right side spacing does not show up , if the text it too long.
-(void)awakeFromNib{
[super awakeFromNib];
view1 =[[UILabel alloc] init];
view1.translatesAutoresizingMaskIntoConstraints=NO;
[self addSubview:view1];
view1.text= #"Hello";
NSDictionary *constraintViews=
#{#"view1":view1};
NSDictionary *metrics=#{#"spacing":#(15)};
NSArray *hConstraints=[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-spacing-[view1]-spacing-|" options:NSLayoutFormatAlignAllCenterX metrics:metrics views:allViews];
NSArray *vConstraints=[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-spacing-[view1(30)]" options:0 metrics:metrics views:constraintViews];
[self addConstraints:hConstraints];
[self addConstraints:vConstraints];
}
1)
Update the horizontal constraints like so:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-spacing-[view1]-spacing#751-|" options:NSLayoutFormatAlignAllCenterX metrics:metrics views:constraintViews];
Try adding the line below:
[view1 setContentHuggingPriority:UILayoutPriorityHigh forAxis:UILayoutConstraintAxisHorizontal];
2) I always set the numberOfLines property of a label to 0 by default, so that the label will autoresize vertically if the text needs to be shown in two or more lines. That being said, you would need to remove the fixed height constraint and the label will be the size of the it's contents like so:
view1.numberOfLines = 0;
NSArray *vConstraints=[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-spacing-[view1]" options:0 metrics:metrics views:constraintViews];
I hope this helps.
I used this generic method for applying constraints of childView wrt to ParentView.Just pass your views to this method.
+ (void)applyConstraints:(UIView *)pChildView withSuperView:(UIView *)pParentView {
pChildView.translatesAutoresizingMaskIntoConstraints = NO;
// Width.
CGFloat widthValue = pParentView.frame.size.width;
[pParentView addConstraint:[NSLayoutConstraint constraintWithItem:pChildView attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual toItem:pParentView
attribute:NSLayoutAttributeWidth multiplier:1.0 constant:widthValue]];
// Height.
CGFloat heightValue = pParentView.frame.size.height;
[pParentView addConstraint:[NSLayoutConstraint constraintWithItem:pChildView attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual toItem:pParentView
attribute:NSLayoutAttributeHeight multiplier:1.0 constant:heightValue]];
// X margin.
[pParentView addConstraint:[NSLayoutConstraint constraintWithItem:pChildView attribute:NSLayoutAttributeCenterXWithinMargins
relatedBy:NSLayoutRelationEqual toItem:pParentView
attribute:NSLayoutAttributeCenterXWithinMargins multiplier:1.0 constant:0]];
// Y margin.
[pParentView addConstraint:[NSLayoutConstraint constraintWithItem:pChildView attribute:NSLayoutAttributeCenterYWithinMargins
relatedBy:NSLayoutRelationEqual toItem:pParentView
attribute:NSLayoutAttributeCenterYWithinMargins multiplier:1.0 constant:0]];
}
I have a simple UIView that I want to make the same width as the containing View's width. I want to do this programatically.
I can add a constraint in the containing View that makes the SubView's Width equal to the width of container. The C# is because i am using Xamarin iOS but this AutoLayout question is not specific to that.
View.AddConstraint(NSLayoutConstraint.Create(subView,
NSLayoutAttribute.Width,
NSLayoutRelation.Equal,
this.View,
NSLayoutAttribute.Width,
1.0f, 0.0f));
However it feels more natural to control this from within the SubView as it view will always be full width. How would I do that?
When I try and create the constraint from within the SubView I use this.SuperView as the Relation but it does not work. It throws the following Exception
NSInternalInconsistencyException Reason: Unexpected use of internal
layout attribute.
I got the same NSInternalInconsistencyException when trying to add a constraint involving the superview to which I wasn't attached yet. So maybe make sure that you attach first to the superview.
As per your question about how to set UIView size similar to superView.
You can set constraints by using two different ways.
I’ve created view and added it subview to superView.
UIView *redView;
redView = [UIView new];
[redView setBackgroundColor:[UIColor redColor]];
[redView setAlpha:0.75f];
[redView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:redView];
[self.view setBackgroundColor:[UIColor blackColor]];
1.) By using visual format.
NSDictionary *dictViews = #{#"red":redView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[red]-0-|" options:0 metrics:0 views:dictViews]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[red]-0-|" options:0 metrics:0 views:dictViews]];
2.) By using layout attributes.
Here constraintWithItem:redView - is subview to which we want to set constraints and toItem:self.view - is out superview according to which we need to set constraints.
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeHeight multiplier:1.0 constant:1.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeWidth multiplier:1.0 constant:1.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:1.0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:redView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:1.0]];
Hope this will be helpful to you. Happy Coding.
I have a detail view in a story board. I want to load another Nib in that view when a certain condition is met. But when I do, the auto layout its all screwed you.
I am trying to display the UIView above the UIWebView like an overlay view. I want the UIView to have the same ratio of the device with a maximum height of 400 between the top and bottom layout guide.
Here is the code I used to load the Nib:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.navigationController.navigationBar setTintColor:[UIColor whiteColor]];
// Do any additional setup after loading the view, typically from a nib.
UINib *s = [UINib nibWithNibName:#"Square1" bundle:nil];
NSArray *array = [s instantiateWithOwner:self options:nil];
StopView *stopView = (StopView *)[array objectAtIndex:0];
[stopView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.view addSubview:stopView];
id topGuide = self.topLayoutGuide;
id bottomGuide = self.bottomLayoutGuide;
UIWebView *webView = self.detailWebView;
NSDictionary *views = NSDictionaryOfVariableBindings(stopView, topGuide, bottomGuide, webView);
// this is here to stop the auto layout from reporting that the guides has
// ambiguous layout
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[topGuide]|" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[bottomGuide]|" options:0 metrics:nil views:views]];
// center the stop view in the super view, both lines below are needed
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-(>=12)-[stopView(<=400)]-(>=12)-|"
options: 0
metrics:nil
views:views]];
// set the height to a ratio of the width
NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeWidth
relatedBy:0 toItem:stopView
attribute:NSLayoutAttributeHeight
multiplier:0.66667f constant:0];
[self.view addConstraint:con2];
// center the Stop View X,Y with the super view
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1.0f constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:stopView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1.0f constant:0]];
NSLog(#"Calling configureView from viewDidLoad");
[self configureView];
}
Here are some screen shots:
As you can see in the third screen shot, my background is not showing. And you can see the T from the UILabel that placed at the top in design mode.
What am I doing incorrectly?
For a vertical constraint, you need to put a "V:" in front of the string. To get a view to be 400 max, but be as big as it can given the top and bottom spacing constraints for a smaller screen, you need to use the priority of the constraint,
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=12)-[stopView(==400#900)]-(>=12)-|"
options: 0
metrics:nil
views:views]];
The system will try to make the height of stopView as close to 400 as possible while maintaining a spacing of at least 12 to the top and bottom.
What I want is to add an image as a subview, then align it centered along the X axis and 10 points from the bottom of the superview. I need to use Auto Layout only, and preferably visual formatting language.
- (void)viewDidLoad
{
[super viewDidLoad];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
[self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.imageView setImage:[UIImage imageNamed:#"06-arrow-south"]];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:self.imageView];
[self addConstraints];
self.imageView.layer.borderColor = [[UIColor redColor] CGColor];
self.imageView.layer.borderWidth = 1.0;
}
- (void)addConstraints {
NSDictionary *viewsDictionary = #{#"arrowImage":self.imageView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[arrowImage(==40)]-|"
options:0
metrics:nil
views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[arrowImage(==40)]-10-|"
options:0
metrics:nil
views:viewsDictionary]];
}
Here's what I'm getting:
V:|-[arrowImage]-10-|
This aligns the image view so that it is the standard length (20pt) from the top of its superview, and 10 from the bottom. What you want is to PIN it to the bottom only:
V:[arrowImage]-10-|
I'm not sure that centering in the superview can be done with visual format, but you can create a single constraint to center it:
[self.view addConstraint:
[NSLayoutConstraint
constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
There's no need to set the height or width of the image view; its size will be determined from its content.
So, here's the full code for your addConstraints method:
- (void)addConstraints {
[self.view addConstraint:
[NSLayoutConstraint
constraintWithItem:self.imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
NSDictionary *viewsDictionary = #{#"arrowImage":self.imageView};
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[arrowImage]-10-|"
options:0
metrics:nil
views:viewsDictionary]];
}
What you currently doing is saying arrowImage should be the full size of the view minus 20px on left, right and top but be minus 10px from bottom.
The to center on x do the following.
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:arrowImage attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0];
Then as #Austin points out remove the need to be minus 8 from top and be minus 10 from the bottom:
V:[arrowImage]-10-|
Btw its minus 20 as default when you connect a sibling view to a parent: (see comment below )
|-[
I have just started using Auto Layout for my latest project and I was wondering what would be the most efficient way of laying out the following table cell:
Views A and B are both UILabels. C is a fixed size image and the view under A is an image that may or not be present. I am able to easily lay out A, B and C. But if the image under A is present, A's height needs to shrink proportionately and the image needs to fit underneath so that both are centered horizontally in the contentView.
I am trying to lay the entire cell out using code and the Visual Format language and have gotten quite close so far. The only problem is that the A and it's accompanying image aren't centered vertically in the container. You can see how far I have gotten in the image below:
And here is the code that I am using in my updateConstraints method. Note that with this code, I don't get an ambiguous layout:
NSDictionary *views = NSDictionaryOfVariableBindings(viewA, viewB, viewC);
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[viewA]-[viewB]-(>=8)-[viewC]-|"
options:0
metrics:nil
views:views]];
NSDictionary *metrics = #{#"width":#(40.0f), #"height":#(40.0f), #"priority":#(UILayoutPriorityRequired)};
[viewC addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[viewC(==width#priority)]"
options:0
metrics:metrics
views:#{#"viewC": _merchantLogo}]];
[viewC addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[viewC(==height#priority)]"
options:0
metrics:metrics
views:views]];
[viewA addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[viewA(>=75#750)]"
options:0
metrics:nil
views:views]];
[viewB addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[viewB(>=115#500)]"
options:0
metrics:nil
views:views]];
[self.contentView addConstraints:#[[NSLayoutConstraint constraintWithItem:viewC
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f],
[NSLayoutConstraint constraintWithItem:viewB
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]]];
if (!viewD) {
[self.contentView addConstraints:#[[NSLayoutConstraint constraintWithItem:viewA
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.contentView
attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]]];
} else {
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[viewA][viewD]"
options:NSLayoutFormatAlignAllLeft
metrics:nil
views:NSDictionaryOfVariableBindings(viewA, viewD)]];
}
One of my ideas was to put A and the image below it in a container view and then lay them out within that view. But that seems kind of inefficient and I first want to make sure this isn't possible without using a container view.
So...
1.
Format
#"|-[_viewA(<=75)]-[viewB]-[viewC(==60)]-|"
Options
NSLayoutFormatAlignAllTop
2.
Format
#"V:|-[viewA]-[imageView(==10)]-|"
Options
NSLayoutFormatAlignCenterX | NSLayoutFormatAlignAllLeft
3.
Add individual constraints to constrain bottom of image view to bottom of viewB and viewC.
[NSLayotuConstraint constraintWithItem:viewB
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:imageView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0];
and other one...
This should give you what you want.