Adding programatically created views into scrollview vertically (Linear layout in iOS) - ios

I want to add programatically created UIViews into scrollView with auto layout constraints. Like vertical linear layout in Android.
(In objective c not swift)
I have scrollview inside view controller in storyboard. So basically i want to create and add several views in vertical layout with no spaces into that scrollview. And i want to set container size of the scroll view dynamically according to the view heights.
Each view has label inside and each view needs to set its height dynamically according to text size. But probably i need to come to that later.
for (int i=0; i<10; i++)
{
UIView *viewOne = UIView.new;
[viewOne setTranslatesAutoresizingMaskIntoConstraints:NO];
viewOne.backgroundColor = [UIColor redColor];
NSDictionary *viewsDictionary = #{#"viewOne" : viewOne};
NSDictionary *metricsDictionary = #{#"horizontalSpacing" : #10};
[self.scrollview addSubview:viewOne];
NSArray *horizontalConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-horizontalSpacing-[viewOne]-horizontalSpacing-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:metricsDictionary
views:viewsDictionary];
NSArray *const_Height = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[viewOne(50)]"
options:0
metrics:nil
views:viewsDictionary];
[viewOne addConstraints:const_Height];
[self.scrollview addConstraints:horizontalConstraints];
}
With that code i can add views but i need to add one under the other.

In case of using AutoLayout in the context of a UIScrollView I would recommend using a ContentView insider your UIScrollView. Just add them to the ViewControllers View inside the viewDidLoad function.
#interface YourViewController ()
#property (nonatomic, strong) UIScrollView *dataScrollView;
#property (nonatomic, strong) UIView* contentView;
#end
#implementation YourViewController
#synthesize dataScrollView, contentView;
- (void) viewDidLoad {
[super viewDidLoad];
dataScrollView = [[UIScrollView alloc] init];
contentView = [[UIView alloc] init];
// adding the Views programmatically to the hierarchy
[self.view addSubview:dataScrollView];
[dataScrollView addSubview:contentView];
// don't translate the AutoresizingMask into constraints
dataScrollView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.translatesAutoresizingMaskIntoConstraints = NO;
// backgroundColor as you wish?
dataScrollView.backgroundColor = [UIColor clearColor];
contentView.backgroundColor = [UIColor clearColor];
[dataScrollView setScrollEnabled:YES];
[dataScrollView setAlwaysBounceVertical:YES];
NSDictionary* viewsDictionary = NSDictionaryOfVariableBindings(dataScrollView, contentView);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[dataScrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[dataScrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[dataScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[contentView(==dataScrollView)]|" options:0 metrics: 0 views:viewsDictionary]];
[dataScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[contentView]|" options:0 metrics: 0 views:viewsDictionary]];
// see below
// [self setUpViews];
}
This Code will do the Trick for one single view. Add your required Views as Subview to the contentView and set the Constraints.
- (void) setUpViews {
UILabel* testLabel = [[UILabel alloc] init];
[testLabel setText:#"Lorem Ipsum"];
testLabel.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview: testLabel];
// clean up your code with this metrics Dictionary
NSDictionary *metrics = #{#"margintop": #40,
#"marginleft": #10,
#"marginright": #10,
#"marginbottom": #20}
// the Views we want to layout with Constraints
NSDictionary *viewsDictionary = #{
#"contentView":contentView,
#"dataScrollView":dataScrollView,
#"testLabel": testLabel}
// Horizontal (testlabel)
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-marginleft-[testLabel]-marginright-|" options:0 metrics: metrics views:viewsDictionary]];
// Vertical
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-margintop-[testLabel]-marginbottom-|" options:0 metrics: metrics views:viewsDictionary]];
}
Referring to your question of adding multiple Views in a for-loop, there are a lot of possible ways. This could be the easiest solution with constraintsWithVisualFormat.
- (void) setUpViews {
NSDictionary *metrics = #{#"margintop": #40,
#"marginleft": #10,
#"marginright": #10,
#"marginbottom": #20,
};
// Alsways like to have contentView and DataScrollView here
NSMutableDictionary* dictViews = [[NSMutableDictionary alloc] initWithDictionary:#{#"contentView":contentView,
#"dataScrollView":dataScrollView}];
// Basic Leading-String for Vertical Constraints
NSString* verticalConstraintsString = #"V:|-margintop-";
for (NSUInteger index = 0; index < 10; index++) {
// Do your Magic here & add your View
UILabel* testLabel = [[UILabel alloc] init];
[testLabel setText:#"Lorem Ipsum"];
testLabel.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview: testLabel];
// Add to global Mutable Views-Dictionary dictViews
[dictViews setObject:testLabel forKey:[NSString stringWithFormat:#"testLabel%lu", (unsigned long)index]];
// add "[testlabel1]" to the vertical Constraints
verticalConstraintsString = [NSString stringWithFormat:#"%#[testLabel%lu]-", verticalConstraintsString, (unsigned long)index];
// Add Horizontal Constraints
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:|-marginleft-[testLabel%lu]-marginright-|", (unsigned long)index] options:0 metrics: metrics views:#{#"testLabel-%lu":testLabel}]];
}
// Trailing-String
verticalConstraintsString = [NSString stringWithFormat:#"%#marginbottom-|", verticalConstraintsString];
NSDictionary *viewsDictionary = [[NSDictionary alloc] initWithDictionary:dictViews];
// finally adding the vertical Constraints
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:verticalConstraintsString options:0 metrics: metrics views:viewsDictionary]];
}
I hope this will help you to get your Views right.

Related

How to draw a horizontal line using UIView and autolayout?

Here is the code I have...this is kind of a simple question...perhaps I shouldn't do it with UIView...but I just wanna draw a decorative horizontal line to separate content. What I am getting is no error the line just does not draw. If I init it with a frame and turn off autolayout, then I do see the line but that's not how I want to implement it since everything is autolayout.
Below the viewDidLoad calls the other methods to create the UI elements and add the constraints.
- (void)viewDidLoad {
[super viewDidLoad];
// Add main layout
[self.scrollView addSubview:self.profileInfoContainer];
// Add UI elements
[self.profileInfoContainer addSubview:self.email];
[self.profileInfoContainer addSubview:self.userName];
[self.profileInfoContainer addSubview:self.hLineSeperator];
[self.profileInfoContainer addSubview:self.navigationContainer];
// Add Constraints
[self addInfoContainerConst];
}
Below i add the constraints for all ui elements in the profileInfoContainer, including the hLineSeperator
- (void)addInfoContainerConst {
// Add constrains
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(_userName, _email, _hLineSeperator, _navigationContainer);
NSDictionary *metrics = #{
#"vTop":#60,
#"vBttm":#5,
#"hLeft":#25,
#"hRight":#25,
#"vMiddle":#5
};
NSArray *constraint_POS_V = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-vTop-[_userName]-vMiddle-[_email]-vMiddle-[_hLineSeperator]-vMiddle-[_navigationContainer]|"
options:0
metrics:metrics
views:viewsDictionary];
NSArray *constraint_POS_H = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-hLeft-[_userName]-hRight-|"
options:0
metrics:metrics
views:viewsDictionary];
NSArray *constraint_POS_H_1 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-hLeft-[_email]-hRight-|"
options:0
metrics:metrics
views:viewsDictionary];
NSArray *constraint_POS_H_2 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_hLineSeperator(1)]|"
options:0
metrics:metrics
views:viewsDictionary];
NSArray *constraint_POS_H_3 = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[_navigationContainer]|"
options:0
metrics:metrics
views:viewsDictionary];
[self.profileInfoContainer addConstraints:constraint_POS_V];
[self.profileInfoContainer addConstraints:constraint_POS_H];
[self.profileInfoContainer addConstraints:constraint_POS_H_1];
[self.profileInfoContainer addConstraints:constraint_POS_H_2];
[self.profileInfoContainer addConstraints:constraint_POS_H_3];
}
Below is the method to create the UIView that is used to draw the line
- (UIView*)hLineSeperator {
if (!_hLineSeperator) {
_hLineSeperator = [UIView new];
_hLineSeperator.translatesAutoresizingMaskIntoConstraints = NO;
_hLineSeperator.backgroundColor = [UIColor grayColor];
}
return _hLineSeperator;
}
It is not required to add constraints to subviews (specifically like the ones you want to create) created in code. You can do it something like,
UIView *horizontalSeperatorViewOnTop = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.(yourView).frame.size.width, 0.5f)];
horizontalSeperatorViewTop.backgroundColor = [UIColor lightGrayColor];
[self.(yourView) addSubview: horizontalSeperatorViewOnTop];
I do this in my code all the time for horizontal or vertical separators and it works great!

How to add constraints programatically to place a label at the left bottom of the screen?

I want to place a label at the bottom left of the screen: So I added the following code:
UILabel *versionLabel = [UILabel new];
versionLabel.text = #"some text";
versionLabel.textColor = [UIColor whiteColor];
versionLabel.font = [UIFont systemFontOfSize:10.0];
[self.view addSubview:versionLabel];
[versionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *views = NSDictionaryOfVariableBindings(versionLabel);
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-3-[versionLabel]"
options:0
metrics:nil
views:views]];
[self.view addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[versionLabel]-3-|"
options:0
metrics:nil
views:views]];
My issue is that the label ends up at the top left always. No way to move it at the bottom when I add constraints.
If you wrap the UITableViewController in a UIViewController, you can layout the label and table view as you see fit.
#import "WrappingViewController.h"
#import "YourCustomTableViewController.h"
#implementation WrappingViewController {
}
- (void)viewDidLoad {
// I instantiate this manually, but you could use an outlet from interface builder
UITableViewController *tableViewController = [YourCustomTableViewController new];
[self addChildViewController:tableViewController];
UIView *tableView = tableViewController.view;
UILabel *label1 = [UILabel new];
label1.text = #"Some text";
label1.font = [UIFont systemFontOfSize:10.0];
label1.translatesAutoresizingMaskIntoConstraints = NO;
tableView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:tableView];
[self.view addSubview:label1];
NSDictionary *views = NSDictionaryOfVariableBindings(tableView, label1);
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|[tableView]|" // make the tableview take the whole width
options:0
metrics:nil
views:views]
];
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"H:|-3-[label1]" // attach the label 3 pixels from the left
options:0
metrics:nil
views:views]
];
[self.view addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:|[tableView]-[label1]-3-|" // attach tableview to top, then space, then label, 3px, then bottom
options:0
metrics:nil
views:views]
];
}
#end

Does it matter which view constraints are added to?

I find it convenient to collect all constraints for a whole view hierarchy and adding them all to the local root view. Is this discouraged and/or a bad idea versus adding constraints to the appropriate views?
Below is a simplification of a common pattern I use. The views are often more complicated with multiple stored constraints that needs updating as views are added or removed.
- (void)updateViewConstraints
{
UIView *content = ...;
UIView *panel = ...;
UILabel *label = ...;
[self.view addSubview:content];
[content addSubview:panel];
[panel addSubview:label];
NSMutableArray *constraints = [NSMutableArray new];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"|[content]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(content)]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"|[panel]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(panel)]];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:#"|[label]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(label)]];
[self.view addConstraints:constraints]
self.storedConstraints = constraints;
[super updateViewConstraints];
}
- (void)invalidateConstraints
{
[self.view removeConstraints:self.storedConstraints];
[self.view needsUpdateConstraints];
}

Single Auto Layout constraint change breaks entire layout

I have a UIViewController which creates this layout:
The part that gives me trouble is the darker area on the bottom, containing the text.
The darker part is a UIScrollView subclass, which creates this layout using Auto Layout:
I have a couple of UILabels ("Omschrijving", "Informatie", and the small labels at the bottom of the view) and a UITextView (the view containing the text starting with "Robert Langdon").
I set the UITextViews height explicitly to 60 points, and when the "Meer" button is tapped, I calculate its full height using boundingRectWithSize:options:attributes:context. I then change its height constraint constant from its prior hardcoded value to the value I've calculated.
That's where it goes wrong. The layout is absolutely fine until the UITextView's height changes. All the content in the UIScrollView subclass seems to move inexplicably. I looked at the view hierarchy with Reveal: .
I've been messing with the constraints for hours now and I can't find a solution.
All the views have translatesAutoresizingMaskIntoConstraints set to NO.
This is iOS 8 on the simulator, with Xcode 6 b4.
My init methods looks like this:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.translatesAutoresizingMaskIntoConstraints = NO;
[self layoutIfNeeded];
[self addExpandButton];
[self setupUI];
[self setupConstraints];
[self layoutIfNeeded];
self.backgroundColor = [UIColor clearColor];
self.textView.backgroundColor = [UIColor clearColor];
self.textView.userInteractionEnabled = NO;
}
return self;
}
setupUI creates all the views and adds them to the scrollView. I've removed some repetitive lines of code for the sake of brevity
- (void)setupUI
{
// Initialization
UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectZero];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 60.0) textContainer:nil];
textView.translatesAutoresizingMaskIntoConstraints = NO;
textView.editable = NO;
textView.textContainerInset = UIEdgeInsetsMake(0.0, -5.0, 0.0, -5.0);
textView.textColor = [UIColor whiteColor];
// init all the UILabels with CGRectZero frames
// ...
// ...
UIView *separator = [[UIView alloc] initWithFrame:CGRectZero];
separator.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.5];
// Set the text
descriptionLabel.text = #"Omschrijving";
textView.text = #"Robert Langdon, hoogleraar kunstgeschiedenis en symboliek, wordt op een nacht wakker in een ziekenhuis in Florence zonder te weten hoe hij daar is beland. Geholpen door een stoïcijnse jonge vrouw, Sienna Brooks, vlucht Langdon en raakt hij verzeild in een duizelingwekkend avontuur. Langdon ontdekt dat hij in het bezit is van een reeks verontrustende codes, gecreëerd door een briljante wetenschapper; een genie dat geobsedeerd is door het einde van de wereld en het duistere meesterwerk Inferno van Dante Alighieri.";
// Set the text of the labels
// ...
// ...
descriptionLabel.font = [UIFont fontWithName:#"ProximaNova-Semibold" size:14.0];
textView.font = [UIFont fontWithName:#"ProximaNova-Regular" size:12.0];
informationLabel.font = [UIFont fontWithName:#"ProximaNova-Semibold" size:14.0];
UIFont *font = [UIFont fontWithName:#"ProximaNova-Regular" size:10.0];
// Set the font of the labels
// ...
// ...
// Add the views to the view hierarchy
[self addSubview:descriptionLabel];
[self addSubview:textView];
[self addSubview:separator];
[self addSubview:informationLabel];
// Add the other labels as subviews
// ...
// ...
[self.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UILabel class]]) {
UILabel *label = (UILabel *)obj;
[label sizeToFit];
label.textColor = [UIColor whiteColor];
}
}];
// Assign the local properties to properties
// ...
// ...
for (UIView *view in self.subviews) {
view.translatesAutoresizingMaskIntoConstraints = NO;
}
}
Now on to the constraints. I have a big method that adds all the constraints called -addConstraints.
- (void)setupConstraints
{
// Omschrijving label top
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-3-[descriptionLabel]-3-[textView]"
options:NSLayoutFormatAlignAllLeading
metrics:nil
views:#{#"descriptionLabel": self.descriptionLabel,
#"textView": self.textView}]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.descriptionLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:3.0]];
// Omschrijving label leading
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-10-[descriptionLabel]"
options:0
metrics:nil
views:#{#"descriptionLabel": self.descriptionLabel}]];
// Text view width
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[textView(==width)]"
options:0
metrics:#{#"width": #220.0}
views:#{#"textView": self.textView}]];
// Text view height
NSArray *textViewHeightConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[textView(==60.0)]"
options:0
metrics:nil
views:#{#"textView": self.textView}];
[self addConstraints:textViewHeightConstraints];
self.textViewHeightConstraints = textViewHeightConstraints;
NSLog(#"%#", self.textViewHeightConstraints);
// Text view expand button
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[textView][expandButton(==12.0)]"
options:NSLayoutFormatAlignAllTrailing
metrics:nil
views:#{#"textView": self.textView, #"expandButton": self.expandButton}]];
// Separator
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[expandButton]-6-[separator]"
options:NSLayoutFormatAlignAllTrailing
metrics:nil
views:#{#"expandButton": self.expandButton, #"separator": self.separator}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[separator(==textView)]"
options:0
metrics:nil
views:#{#"separator": self.separator, #"textView": self.textView}]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[separator(==0.5)]"
options:0
metrics:nil
views:#{#"separator": self.separator}]];
NSString *leftVisualFormatString = #"V:[separator]-6-[informationLabel]-spacing-[languageDescriptionLabel]-spacing-[categoryDescriptionLabel]-spacing-[publisherDescriptionLabel]-spacing-[publishedDateDescriptionLabel]-spacing-[pageCountDescriptionLabel]-|";
NSDictionary *descriptionViews = #{#"separator": self.separator,
#"informationLabel": self.informationLabel,
#"languageDescriptionLabel": self.languageDescriptionLabel,
#"categoryDescriptionLabel": self.categoryDescriptionLabel,
#"publisherDescriptionLabel": self.publisherDescriptionLabel,
#"publishedDateDescriptionLabel": self.publishedDateDescriptionLabel,
#"pageCountDescriptionLabel": self.pageCountDescriptionLabel};
NSDictionary *metrics = #{#"spacing": #1.0};
// All at once: vertical spacing and leading alignment for the labels on the left
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:leftVisualFormatString
options:NSLayoutFormatAlignAllLeading
metrics:metrics
views:descriptionViews]];
// Same, for the righthand labels
NSDictionary *views = #{#"languageLabel": self.languageLabel,
#"categoryLabel": self.categoryLabel,
#"publisherLabel": self.publisherLabel,
#"publishedDateLabel": self.publishedDateLabel,
#"pageCountLabel": self.pageCountLabel};
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[pageCountDescriptionLabel]-20-[pageCountLabel]"
options:0
metrics:nil
views:#{#"pageCountDescriptionLabel": self.pageCountDescriptionLabel,
#"pageCountLabel": self.pageCountLabel}]];
NSString *rightVisualFormatString = #"V:[languageLabel]-spacing-[categoryLabel]-spacing-[publisherLabel]-spacing-[publishedDateLabel]-spacing-[pageCountLabel]-|";
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:rightVisualFormatString
options:NSLayoutFormatAlignAllLeading
metrics:metrics
views:views]];
}
As I said, this works fine until I call this method:
- (void)tappedExpandButton:(id)sender
{
if (self.textViewHeightConstraints.count == 1) {
NSLayoutConstraint *constraint = self.textViewHeightConstraints.firstObject;
CGFloat newHeight = [self.textView.text boundingRectWithSize:CGSizeMake(self.textView.textContainer.size.width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:#{NSFontAttributeName: self.textView.font}
context:nil].size.height;
constraint.constant = ceilf(newHeight);
[self layoutIfNeeded];
}
}
Thanks already!
I think I've figured it out. For some reason I don't yet understand, the containing UIScrollView's frame changed when adding the constraint. That still seems really weird to me, since I think only its contentSize should change, but adding constraints from the dark area (which is a UIView) to the UIScrollView fixed it. This is the code:
[self.darkEffectView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[descriptionView]|"
options:0
metrics:nil
views:#{#"descriptionView": self.descriptionView}]];
[self.darkEffectView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[descriptionView]|"
options:0
metrics:nil
views:#{#"descriptionView": self.descriptionView}]];

Align two uiviews below each other using autoloayout

I am trying desperately to align two views below each other with autolayout hoping to animate them in the future and with a relative position this makes it more rigid than just setting frames and updating etc...
So the code:
// create two views
UIView *one = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
UIView *two = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 50, 50)];
// add some color
[one setBackgroundColor:[UIColor whiteColor]];
[two setBackgroundColor:[UIColor redColor]];
// add them to self ( self = uiview )
[self addSubview:one];
[self addSubview:two];
// make dict
NSDictionary *views = NSDictionaryOfVariableBindings(one, two);
// add constraints to make the views as wide as the container ( self )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[one]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[two]|"
options:0
metrics:nil
views:views]];
// add constraint to make the second ( two ) item 10px below the first ( one )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[one]-10-[two]"
options:0
metrics:nil
views:views]];
```
<-- the result
<-- the goal
What am i doing wrong?
Thanks in advance!
These two lines explain it all:
UIView *one = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
UIView *two = [[UIView alloc] initWithFrame:CGRectMake(0, 150, 50, 50)];
This is exactly what you are seeing as a result: both views are 50x50 in size, and view two is placed at x=0 and y=150.
When working with autolayout, you need to forget everything about frames and rects. State what constraints you want, for each view and between views, and let autolayout create the frames behind the scenes.
Declare views for autolayout like this:
UIView *one = [[UIView alloc] init];
one.translatesAutoresizingMaskIntoConstraints = NO;
UIView *two = [[UIView alloc] init];
two.translatesAutoresizingMaskIntoConstraints = NO;
You should not set frames while using constraints. That's what constraints are for. Constraints are the cause, frame is the effect
// create two views
UIView *one = [[UIView alloc] init];
UIView *two = [[UIView alloc] init];
one.translatesAutoresizingMaskIntoConstraints = NO;
two.translatesAutoresizingMaskIntoConstraints = NO;
// add some color
[one setBackgroundColor:[UIColor whiteColor]];
[two setBackgroundColor:[UIColor redColor]];
// add them to self ( self = uiview )
[self addSubview:one];
[self addSubview:two];
// make dict
NSDictionary *views = NSDictionaryOfVariableBindings(one, two);
// add constraints to make the views as wide as the container ( self )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[one]|"
options:0
metrics:nil
views:views]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[two]|"
options:0
metrics:nil
views:views]];
// add constraint to make the second ( two ) item 10px below the first ( one )
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[one]-10-[two]|"
options:0
metrics:nil
views:views]];
Note: I have changed a constraint in the last line. Your constraints did not specify the height of view two. It would have given you an "AMBIGUOUS" layout.

Resources