iOS How to auto layout a view 3 x screen width? - ios

I tried to place a UIView which is 3x the screen width (or 3x UIScrollView's width) in UIScrollView.
UIScrollView's width equal to the screen width.
How do I do this in AutoLayout?

If you want to do it in code via pure AutoLayout, here's how you'd do it:
- (void)layoutUserInterface {
// Placing the scrollView using AutoLayout
[self.view addSubview:self.scrollView];
// Note: The "H:" is optional, but I like to be clear
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[scrollview]|"
options:0
metrics:nil
views:#{#"scrollview": self.scrollView}]];
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[scrollview]|"
options:0
metrics:nil
views:#{#"scrollview": self.scrollView}]];
// Placing the "wideView" using AutoLayout
[self.scrollView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[wideView]"
options:0
metrics:nil
views:#{#"wideView": self.wideView}]];
[self.scrollView addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[wideView]"
options:0
metrics:nil
views:#{#"wideView": self.wideView}]];
// Setting up the 3x width constraint
[self.scrollView addConstraint:
[NSLayoutConstraint
constraintWithItem:self.wideView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeWidth
multiplier:3.0f
constant:0.0f]];
// Figure out your "wideView's" height requirements
}

Assign a value for your views's width constraint and make an IBOutlet for this in your header file.
In your m file's viewDidLoad method get the width of the current device's screen, multiply by 3 and assign to your constraint's constant. And finally call your view's layoutIfNeeded method.
Is this clear enough or do you want me to make it more clear?

Related

how to do uilabel programmatically in auto layout

I am doing a project in auto layout in Xcode 6, I am adding a label programmatically its working perfectly in 4s,5,5s but in 6 and 6 plus is not working. can anyone help me, I am new to auto layout.Below is my coding.
UIScrollView *scroll = [[UIScrollView alloc]init];
[scroll setTranslatesAutoresizingMaskIntoConstraints:NO];
scroll.contentSize=CGSizeMake(480, 600);
[centerView addSubview:scroll];
NSDictionary *scrolldic = #{#"scrollview":scroll};
NSArray *scroll_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[scrollview(480)]" options:0 metrics:Nil views:scrolldic];
NSArray *scroll_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[scrollview(480)]" options:0 metrics:Nil views:scrolldic];
[scroll addConstraints:scroll_H];
[scroll addConstraints:scroll_V];
NSArray *scroll_posH = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0-[scrollview]" options:0 metrics:Nil views:scrolldic];
NSArray *scroll_posV = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[scrollview]" options:0 metrics:Nil views:scrolldic];
[self.view addConstraints:scroll_posH];
[self.view addConstraints:scroll_posV];
UILabel *header = [[UILabel alloc]init];
[header setTranslatesAutoresizingMaskIntoConstraints:NO];
header.backgroundColor = [UIColor blueColor];
[scroll addSubview:header];
NSDictionary *headerdic = #{#"header":header};
NSArray *header_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[header(150)]" options:0 metrics:Nil views:headerdic];
NSArray *header_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[header(30)]" options:0 metrics:Nil views:headerdic];
[header addConstraints:header_H];
[header addConstraints:header_V];
NSArray *header_posH = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-80-[header]" options:0 metrics:Nil views:headerdic];
NSArray *header_posV = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-20-[header]" options:0 metrics:Nil views:headerdic];
[self.view addConstraints:header_posH];
[self.view addConstraints:header_posV];
see the above image in 4s label is in center is correct , but in 6 it move to some left, what is the problem can any one help me.
To get it to work, it is also important how you want the layout to look on different devices. Should the label always have the width 150 and be centered or should it always have a 80 left and right padding?
This is what you have to decide but the constraints would look like this:
First case (same width and centered) :
NSLayoutConstraint *centerXConstraint = [NSLayoutConstraint constraintWithItem:header attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem: scroll attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0];
[scroll addConstraint:centerXConstraint];
Second case (keep the padding between devices):
[scroll addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-leftPadding-[header]-leftPadding-|" options:0 metrics:#{ #"leftPadding": #(80) } views:#{ #"header": header }]];
Let me know hot it goes or if you need more help.
Try adding your constraint like this
NSLayoutConstraint *leadingConstraint= [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yourLabel attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0];
NSLayoutConstraint *topConstraint= [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:_yourLabel attribute:NSLayoutAttributeTop multiplier:1.0 constant:0];
[self.contentView addConstraints:#[leadingConstraint,topConstraint]];
The issue seems to be that you have hard coded the values in. This is fine for iPhone 4 and 5 as they both have screens with width of 320. This means your hard coded value of 80 keeps it in the middle.
iPhone 6 though has a wider screen
iPhone 4 and 5
|---80---|--150--|---90---|
You can see this with a screenshot of your screenshot above:
Here I have added two green lines both the same length, although you think your label is in the middle on iPhone 4 and 5 it actually isn't.
What you need to do is one of two things:
Use NSLayoutConstraints to fix the view in the middle instead of setting values. There are various layout constraints that put it in the middle.
You can use centreX for this
NSLayoutConstraint * centreConstraint = [NSLayoutConstraint constraintWithItem:self.contentView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yourLabel attribute:**NSLayoutAttributeCenterX** multiplier:1.0 constant:0];
Then add this constraint (NSLayoutConstraint can be quite fiddly so make sure you test it as this might not work)
This sets the label in the middle of the screen (hence the centreX - meaning centre of the x-axis)
The other way, not recommended if you are trying to have both landscape and portrait orientations, would be to calculate the width of the screen and so enter an accurate half way padding value.
You can calculate the width of the screen with
[UIScreen mainScreen].bounds.size.width
Then you can calculate the value you need it to be Eg in your case it would be
// Your label is 150 pixels wide so the space on each side is the width less 150 but half as you have the same amount on each side.
([UIScreen mainScreen].bounds.size.width - 150)/2
This is a way to simply calculate the value needed and is not as good as using NSLayoutConstraint. It is much simpler but much less versatile. I only include it for completeness.
My advice, look on some tutorials for NSLayoutConstraints this one is good and get better understanding of what you are constraining.

Changing programmatic/VFL constraints on-the-fly

I'm manually adding a subview to a view and am positioning it with constraints...
UIView *superview = self.view;
UIView *subview = self.subview;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(superview, subview);
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(0)-[subview]-(0)-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:viewsDictionary]];
[self.superview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(0)-[subview]-(0)-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:viewsDictionary]];
This positions my subview neatly in the superview. However, at a later time I would like to apply a margin around my subview (with animation) so that it is inset by 100. So in effect, my constraints in visual format language would be...
"H:|-(100)-[subview]-(100)-|"
"V:|-(100)-[subview]-(100)-|"
How can I attach variables to my 'margin' value so that I can transition between the two types of display for the subview?
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(0)-[subview]-(0)-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:viewsDictionary]
Will return an array with the constraints in the same order that they were specified, you can look for it inside the array and then change its constant value.
But I think the easiest way is to create a reference to it like so (you can store it in an internal variable or property):
NSLayoutConstraint* space = [NSLayoutConstraint constraintWithItem:self.button1 attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual toItem:self.button2
attribute:NSLayoutAttributeLeft multiplier:1.0 constant:12.0];
and add it like this:
[self.view addConstraints:#[space]];
then you can change space.constant to some value. and animate it like so:
[UIView animateWithDuration:1.0 animations:^{
// Make all constraint changes here
[self.view layoutIfNeeded];}];
Other approach would be to remove all constraints and add VFL constraints with updated values, and then perform layoutIfNeeded inside the animation block as above.

Creating constraint to center label in view

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);
}];

How to resize custom UITableView separators on landscape and prevent from disappearing

I've decided to programmatically create my own UITableView separator lines because I need fine control over displaying a separator above and/or below each individual UITableViewCell. My tableView has static cells, so I do not create the separators in cellForRowAtIndexPath. Instead, I have propertys for each cell and in viewDidLoad, I add a top and/or bottom separator as needed. It's working, until I rotate to landscape and then the separator line does not stretch to fill the screen - it of course remains the same width it was when created. I'm not seeing how I can automatically adjust them to fit the width of the screen.
I tried adding Auto Layout constraints (leading, trailing, top/bottom), but for some reason it's not working - the width does not change, but there are no error messages logged to indicate anything is wrong with the constraints. The separator lines also sometimes disappear upon scroll or rotate, and if I comment out the auto layout constraints then they do not disappear.
So how can I make my custom cell separators always stretch to fill the device width upon rotation, and how do I prevent them from disappearing?
If it would be easier/better to create my custom cell separators in a different way, I am willing to do that. I just don't know how this can be done aside from my approach when the cells are static. I considered creating the views in the Storyboard, and setting up the constraints visually, but would that not be the equivalent of what I'm doing programmatically? If they were dynamic cells I would do it in cellForRowAtIndexPath.
//In viewDidLoad:
[self addTopSeparatorForCell:self.myCell];
//Helper method
- (void)addTopSeparatorForCell:(UITableViewCell *)cell {
UIView *topSeparator = [[UIView alloc] initWithFrame:CGRectMake(15, 1, cell.contentView.frame.size.width, 0.5)];
//add CALayer to preserve line separator visibility when row is highlighted
CALayer *backgroundColorLayer = [CALayer layer];
backgroundColorLayer.frame = topSeparator.bounds;
backgroundColorLayer.backgroundColor = [UIColor colorWithWhite:204/255.0f alpha:1].CGColor;
[topSeparator.layer addSublayer:backgroundColorLayer];
[cell.contentView addSubview:topSeparator];
//add auto layout constraints
topSeparator.translatesAutoresizingMaskIntoConstraints = NO;
NSLayoutConstraint *cn = nil;
cn = [NSLayoutConstraint constraintWithItem:topSeparator
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:15];
[cell.contentView addConstraint:cn];
cn = [NSLayoutConstraint constraintWithItem:topSeparator
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
[cell.contentView addConstraint:cn];
cn = [NSLayoutConstraint constraintWithItem:topSeparator
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:1];
[cell.contentView addConstraint:cn];
}
EDIT: Thanks to # user1966109, we've been able to solve the issue with the lines not extending to fill the width, and now they are preserved when highlighting a cell. But one issue still remains that I haven't been able to solve, since I'm not sure why it's occurring. The separator lines disappear after scrolling down the scrolling back up. It's related to the auto layout constraints though because a previous solution which had other issues did not exhibit this problem. Here's the current solution that causes the lines to disappear. I'd appreciate it if someone knows how to prevent this problem (and preserve the other issues already resolved).
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(15#750)-[myView]-(-47#750)-|" options:0 metrics:0 views:viewsDictionary]];
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[myView(2)]-(-2#750)-|" options:0 metrics:0 views:viewsDictionary]];
You should not mix initWithFrame and Auto Layout. You can have a good result with a few lines using Visual Format Language for Auto layout:
//#interface TableViewController ()
#property (weak, nonatomic) IBOutlet UITableViewCell *cell;
//#implementation TableViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
[self.cell.contentView addSubview:myView];
myView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(myView);
[self.cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[myView]|" options:0 metrics:0 views:viewsDictionary]];
[self.cell.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[myView(2)]" options:0 metrics:0 views:viewsDictionary]];
}
This handles rotation perfectly.
Edit!
Set the following constraints if using a accessory view:
//Set a negative value to the trailing space in order to display myView under the accessory view
//Those constraints work for both self.cell and self.cell.contentView (kind of odd)
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(15#750)-[myView]-(-47#750)-|" options:0 metrics:0 views:viewsDictionary]];
[self.cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[myView(2)]-(-2#750)-|" options:0 metrics:0 views:viewsDictionary]];
With the initial help of user1966109, I have figured out constraints that address all of the problems and are working well:
[cell addConstraint:[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:indent]];
[cell addConstraint:[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0.0]];
[cell addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[imageView(0.5)]" options:0 metrics:0 views:viewsDictionary]];

Resize subviews in Autolayout visual form

I've failed on migrating my app from Autoresize to Autolayout mechanism. All I need is a view with dynamic size and subviews that will be connected to hte left top corner with the same size as their superview. In IB I've set size for PieView (My superview) as 186x186. And let the IB to generate all needed Constraints (I will create new ones in code):
PieView.m -updateConstraints()
- (void)updateConstraints {
[super updateConstraints];
[self removeConstraintsAffectingViewAndSubviews];
[self setTranslatesAutoresizingMaskIntoConstraints:NO];
CGFloat sizeCoef = 0.7f;
CGFloat percent = [self.category getFillinPercent] / 100.0f;
percent = percent > 1.0f ? 1.0f : percent;
sizeCoef += (1.0f - sizeCoef) * percent;
NSLayoutConstraint *width = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.f];//kDiameter * sizeCoef];
NSLayoutConstraint *height = [NSLayoutConstraint constraintWithItem:self
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0f
constant:100.0f];//kDiameter * sizeCoef];
//_imgEmptyCircle, _imgFullCircle - UIImageViews
NSDictionary *views = NSDictionaryOfVariableBindings(_imgEmptyCircle, _imgFullCircle, self);
NSDictionary *metrics = #{#"height":#100.0};
[self addConstraints:#[width, height]];
NSString *visualForm = #"H:|[_imgEmptyCircle(height)][_imgFullCircle(height)]|";
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualForm
options:0
metrics:metrics
views:views];
visualForm = #"V:|[_imgEmptyCircle(height)][_imgFullCircle(height)]|";
NSArray *verticalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:visualForm
options:0
metrics:metrics
views:views];
[self addConstraints:verticalConstraints];
[self addConstraints:horizontalConstraints];
}
The result really surprised me. Blue one displayed correctly as I want, but green one has strange behavior.
Off course I have error: Unable to simultaneously satisfy constraints.
First imageView:
NSIBPrototypingLayoutConstraint:0xa2795d0 'IB auto generated at build time for view with fixed frame' V:[UIImageView:0x8a37150(186)]>
NSLayoutConstraint:0xa0305c0 V:[UIImageView:0x8a37150(100)]
Second imageView: the same one
Probable for PieView:
(
NSLayoutConstraint:0x8a40750 V:[CEPieView:0x8a36e50(100)],
NSLayoutConstraint:0xa030570 V:|-(0)-[UIImageView:0x8a37150] (Names: '|':CEPieView:0x8a36e50 ),
NSLayoutConstraint:0xa0305c0 V:[UIImageView:0x8a37150(100)],
NSLayoutConstraint:0xa0305f0 V:[UIImageView:0x8a37150]-(0)-[UIImageView:0xa277cf0]>
NSLayoutConstraint:0xa030620 V:[UIImageView:0xa277cf0(100)],
NSLayoutConstraint:0xa030650 V:[UIImageView:0xa277cf0]-(0)-| (Names: '|':CEPieView:0x8a36e50 )
)
Please, give me an advise how to fix it. How to create and old autoresizing mask(UIViewAutoresizingFlexibleHeight, UIViewAutoresizingFlexibleWidth) with the help of AutoLayout mechanism. Any help will be appreciated.
The way you're setting the constraints is wrong. Basically you're asking the superview to be 100pts by 100pts, and to contains two subviews that are each 100pts by 100pts and are beside AND on top of each other. So first your superview would have to be 200pts wide and/or tall, and then you can't have two view that are both beside each other and on top.
What you really want to do (I think) is having:
NSString *visualForm = #"H:|[_imgFullCircle]|"; // No need to explicitly set the height
Then add the corresponding constraint, then:
visualForm = #"H:|[_imgEmptyCircle(height)]|";
And add another constraint for this, and do the same for vertical constraints.
Basically you'd end up with:
//_imgEmptyCircle, _imgFullCircle - UIImageViews
NSDictionary *views = NSDictionaryOfVariableBindings(_imgEmptyCircle, _imgFullCircle, self);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_imgEmptyCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_imgFullCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imgEmptyCircle]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_imgFullCircle]|"
options:0
metrics:nil
views:views]];

Resources