UIScrollView with Auto Layout Constraints: Auto Content Size Calculation - ios

I'm having troubles with UIScrollView using auto layout constraints. I have the following view hierarchy, with constraints set through IB:
- ScrollView (leading, trailing, bottom and top spaces to Superview)
-- ContainerView (leading, trailing, bottom and top spaces to ScrollView)
--- Button 1 (full width, **top space to ContainerView**)
--- Button 2 (full width, below Button 1)
--- Button n (full width, below Button n-1, **bottom space to ContainerView**)
I want a simple scrollabel list of buttons. Here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor redColor]];
[self.contentView setBackgroundColor:[UIColor yellowColor]];
UIView *lastView= self.contentView; // use for top constraint
NSInteger topBottomMargin= 10, leftRightMargin= 16;
for (int i=0; i<10; i++) {
UIButton *button= [UIButton buttonWithType:UIButtonTypeSystem];
button.translatesAutoresizingMaskIntoConstraints= NO;
[button setTitle:[NSString stringWithFormat:#"Button %d", i] forState:UIControlStateNormal];
[self.contentView addSubview:button];
// add constraints
// top
[self.contentView addConstraint:[NSLayoutConstraint
constraintWithItem:lastView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:button
attribute:NSLayoutAttributeTop
multiplier:1.0 constant:-topBottomMargin]];
// left
[self.contentView addConstraint:[NSLayoutConstraint
constraintWithItem:self.contentView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:button
attribute:NSLayoutAttributeLeading
multiplier:1.0 constant:-leftRightMargin]];
// right
[self.contentView addConstraint:[NSLayoutConstraint
constraintWithItem:self.contentView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:button
attribute:NSLayoutAttributeTrailing
multiplier:1.0 constant:leftRightMargin]];
lastView= button;
}
// bottom
[self.contentView addConstraint:[NSLayoutConstraint
constraintWithItem:self.contentView
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:lastView
attribute:NSLayoutAttributeBottom
multiplier:1.0 constant:topBottomMargin]];
}
It seems the height of contentView is 0! But there are constraints both for top and bottom of it. It should be like this:
But with my code it's like this. Any Help would be great.

You can add constraint to container view to scroll view as Equal height & equal width. Also when you add constraint to buttons don't forget add bottom constraints to buttons as it will decide the end of scroll view(content size).

Since you are using auto-layout constraints on the contentView, it's height (frame) will be zero in the viewDidLoad method. You should move your code into the viewDidLayoutSubviews method and try to add your buttons there.
You should get the height of the contentView there. Please let me know if that works. Hope this helps.
See this question for reference: iOS AutoLayout - get frame size width

I don't think we can't add auto layout directly to a ContainerView inside ScrollView with Intrinsic Size as Default, so I add ContainerView as subview programmatically:
- (void)viewDidLoad{
[super viewDidLoad];
self.contentView = [[UIView alloc] initWithFrame:CGRectZero];
[self.scrollView addSubview:self.contentView];
//Then add your button here as normal.
//...
}
And Gurtej Singh is right, we have to update the frame in the viewDidLayoutSubviews:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
//Don't for get to update your self.scrollView.contentSize if you want to able to scroll
//...
//Update your contentView frame based on scrollview frame and self.scrollView.contentSize.
self.contentView.frame = self.scrollView.bounds or ....;
}
I just want to help, it might not a good solution, but it work for me.

I found the solution i was looking for here:
[super viewDidLoad];
UIScrollView* sv = [UIScrollView new];
sv.backgroundColor = [UIColor whiteColor];
sv.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:sv];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[sv]|"
options:0 metrics:nil
views:#{#"sv":sv}]];
[self.view addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[sv]|"
options:0 metrics:nil
views:#{#"sv":sv}]];
UILabel* previousLab = nil;
for (int i=0; i<30; i++) {
UILabel* lab = [UILabel new];
lab.translatesAutoresizingMaskIntoConstraints = NO;
lab.text = [NSString stringWithFormat:#"This is label %i", i+1];
[sv addSubview:lab];
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(10)-[lab]"
options:0 metrics:nil
views:#{#"lab":lab}]];
if (!previousLab) { // first one, pin to top
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(10)-[lab]"
options:0 metrics:nil
views:#{#"lab":lab}]];
} else { // all others, pin to previous
[sv addConstraints:
[NSLayoutConstraint
constraintsWithVisualFormat:#"V:[prev]-(10)-[lab]"
options:0 metrics:nil
views:#{#"lab":lab, #"prev":previousLab}]];
}
previousLab = lab;
}
// last one, pin to bottom and right, this dictates content size height
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[lab]-(10)-|"
options:0 metrics:nil
views:#{#"lab":previousLab}]];
[sv addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[lab]-(10)-|"
options:0 metrics:nil
views:#{#"lab":previousLab}]];
// look, Ma, no contentSize!

Related

add subviews generated by autoayout to a parent view

This is my very first program in autolayout.
Basic problem: i am not able to add subviews(a uibutton and a uilabel) to a superview(a containerview).Subviews are just out of bond of superview or say not clipped.
I have added commented in detail to be better understanding of code.
What i want:
i dont care whereever containerview is but i want both subviews to be add in containerview with 0 padding from all sides.
- (void)viewDidLoad {
[super viewDidLoad];
**//create a uibutton with dynamic text(MAX_WIDTH=500, height = 60) and uilabel of fixed size(60, 60).Done
//create pin of fixed 2 pixes between UIButton and UILabel.Done
//put above created views in container view, it will max to 562 width and fix 60 height, so UIButton and UIlabel should fill container view with no top, bottom, left and right.Fail**
//this will be containing my button and my label
UIView *superview = self.view;
UIView *containerView = [UIView new];
[containerView setBackgroundColor:[UIColor blackColor]];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
[superview addSubview:containerView];
//this will be containing my button and my label
UILabel *mylabel = [[UILabel alloc]init];
[mylabel setBackgroundColor:[UIColor redColor]];
[mylabel setTranslatesAutoresizingMaskIntoConstraints:NO];
mylabel.text = #"MyLabel";
UIButton *mybutton = [UIButton
buttonWithType:UIButtonTypeRoundedRect];
[mybutton setTitle:#"My Button ye ye yey yeyeyye yeyey"
forState:UIControlStateNormal];
[mybutton setTranslatesAutoresizingMaskIntoConstraints:NO];
[mybutton setBackgroundColor:[UIColor greenColor]];
[containerView addSubview:mylabel];
[containerView addSubview:mybutton];
NSDictionary * views = NSDictionaryOfVariableBindings(mybutton,mylabel);
//create pin of fixed 2 pixes between UIButton and UILabel.Done
NSArray * horizontalConstraintsforbuttons = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[mybutton(<=500)]-2-[mylabel(60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforbutton = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mybutton(==60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mylabel(==60)]" options:0 metrics:nil views:views];
[containerView addConstraints:horizontalConstraintsforbuttons];
[containerView addConstraints:heightConstraintforbutton];
[containerView addConstraints:heightConstraintforLabel];
//container view specific constraints//**it must be ideally <=562, but then this container view disappears, please hep to fix**
NSArray *widthConstraintForConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[containerView(==560)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(containerView)];
NSArray *heightConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[containerView(==60)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(containerView)];
[superview addConstraints:widthConstraintForConstraint];
[superview addConstraints:heightConstraint];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0]];
[superview addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:superview attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0]];
}
Any suggestion? :)
The VFL for each of your subviews is missing a relationship with the parent view. Autolayout is assuming your constraints should be in relation to the top-level view — what you've defined as self.view.
Here's where your problem is.
NSDictionary * views = NSDictionaryOfVariableBindings(mybutton,mylabel);
//create pin of fixed 2 pixes between UIButton and UILabel.Done
NSArray * horizontalConstraintsforbuttons = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[mybutton(<=500)]-2-[mylabel(60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforbutton = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mybutton(==60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat
First, add your containerView to that dictionary so you can refer to it in VFL:
NSDictionary *views = NSDictionaryOfVariableBindings(mybutton,
mylabel,
containerView);
Then in your VFL, use the pipe operator (|) to tell autolayout to place your subviews in relation to their immediate parent.
NSArray * horizontalConstraintsforbuttons = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mybutton(<=500)]-2-[mylabel(60)]" options:0 metrics:nil views:views];
NSArray * heightConstraintforbutton = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[mybutton(==60)]|" options:0 metrics:nil views:views];
NSArray * heightConstraintforLabel = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[mylabel(==60)]|" options:0 metrics:nil views:views];
I'm not entirely sure what you're trying to do with the horizontal placement of those views, but this should get you back on track. I recommend reading this post on VFL, too.
Edit
I sort of see what you're trying to do now. First, base your values at 1x when working with VFL and autolayout. As an example, a width of 560 is larger than the largest possible iPhone screen:
NSArray *widthConstraintForConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"H:[containerView(==560)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(containerView)];
Let's pretend you just wanted containerView to match the width of the device. That would look like this:
#"H:|[containerView]|"
Those pipe operators outside the containerView are saying that you want the leading (left side) and trailing (right side) space of containerView to be flush with the superview.
Alternatively, let's say you wanted your view to be slightly smaller than the width of the device (560/2).
#"H:[containerView(==280)]"
You're already horizontally centering containerView elsewhere, so it'll appear in the center of it's superview.
Let's then assume you want your red label to (actually) have a width of 60 and your green button to have a width less than or equal to 250 (500/2). That would look like this:
#"H:|[mybutton(<=250)]-2-[mylabel(==60)]|"
Since these are subviews of containerView (and we told that to autolayout earlier), the pipe operators are saying you want
the leading space (left side) of mybutton to be flush with containerView.
the trailing space (right side) of mylabel to be flush with containerView.
Since mylabel has a width of 60, mybutton will be narrower (thanks to <=) to satisfy constraints, depending on the width of containerView.

How to automatically change height of UILabel object and position of other elements below, based on the content of UILabel, with autolayout enabled

I've found some answers related to this topic, but nothing works for me. I've tried setPreferredMaxLayoutWidth:, setting number of lines to 0, setting height constraint
to XYZ, or equal or greater than XYZ... and all that in many different combinations. What could be possibly wrong? Any ideas?
Selected label is the one that needs to change the height based on content. Label below it, and possible other elements below should move down if the label has content that doesn't fit in 1 line. There are no constraint problems reported by IB.
Here's how I've just successfully done it:
I set numberOfLines on the label to 0, so it will grow and shrink as necessary.
I gave the label >= 0 left and right leading/trailing space constraints to the container margins, so it can grow to a maximum width.
I did not put any height constraint on the label. The height will therefore be determined by the content.
I made sure that no vertical constraints on anything below the label were limiting its downward growth.
In particular, bear in mind that if you set a constraint from anything to the bottom of the screen, you'll need to make sure that its priority (or the priority of another vertical constraint in the chain from the label to the bottom) is set to a lower priority than the vertical Content Compression Resistance Priority of the label. This will make sure that the growth of the label's content can overcome the other vertical constraints.
If you are using Auto Layout in code, setting the frame does not work. You have to create the constraint set required for the layout you want. For this case you would need to add height constraints for your UILabel.
Full tutorial on how to do is here : http://www.thinkandbuild.it/learn-to-love-auto-layout-programmatically/
if you want to try to take care of it in code then you can approach it like this. i've laid out something akin to what you have then after 5 seconds it will swap in a new vertical constraint to make one of the labels taller. hope it steers you in the right direction ... or at least a direction!
NSArray * vertConstraint;
UIImageView * imageView = [[UIImageView alloc] init];
UILabel * labelOne = [[UILabel alloc] init];
UILabel * labelTwo = [[UILabel alloc] init];
UILabel * labelThree = [[UILabel alloc] init];
imageView.backgroundColor = [UIColor grayColor];
labelOne.backgroundColor = [UIColor redColor];
labelTwo.backgroundColor = [UIColor blueColor];
labelThree.backgroundColor = [UIColor orangeColor];
[imageView setTranslatesAutoresizingMaskIntoConstraints: NO];
[labelOne setTranslatesAutoresizingMaskIntoConstraints: NO];
[labelTwo setTranslatesAutoresizingMaskIntoConstraints: NO];
[labelThree setTranslatesAutoresizingMaskIntoConstraints: NO];
[self.view addSubview:imageView];
[self.view addSubview:labelOne];
[self.view addSubview:labelTwo];
[self.view addSubview:labelThree];
id topGuide = self.topLayoutGuide;
id bottomGuide = self.bottomLayoutGuide;
NSDictionary * viewsDictionary = NSDictionaryOfVariableBindings(imageView, labelOne,labelTwo,labelThree,topGuide, bottomGuide);
// initial vertical constraints. will be swapped out after 5 seconds (See below
vertConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topGuide]-100-[imageView(==200)]-20-[labelOne(==20)]-20-[labelTwo(==20)]-20-[labelThree(==20)]-(>=5)-[bottomGuide]|" options:0 metrics: 0 views:viewsDictionary];
[self.view addConstraints:vertConstraint];
// horizontal constraints for all the elements
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[imageView(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[labelOne(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[labelTwo(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(>=0)-[labelThree(==200)]-(>=0)-|" options:0 metrics: 0 views:viewsDictionary]];
//additional constraints to center them
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:imageView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:labelOne
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:labelTwo
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:labelThree
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0]];
//delay 5 seconds then swap out vertical constraints
// in this case change the (==20) to (==40) for height of element
// you can edit that string more dynamically to fit your needs
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSArray * newVertConstraint = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[topGuide]-100-[imageView(==200)]-20-[labelOne(==20)]-20-[labelTwo(==40)]-20-[labelThree(==20)]-(>=5)-[bottomGuide]|" options:0 metrics: 0 views:viewsDictionary];
[self.view removeConstraints:vertConstraint];
[self.view addConstraints:newVertConstraint];
[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:1.5 animations:^{
[self.view layoutIfNeeded];
}];
});

iOS Auto Layout When Loading Another Nib

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.

iOS programmatically fix the position of a subview within UIScrollView with Autolayout

I have a UIView that holds a UIScrollView. The UIScrollView contains an MKMapView subview and a placeholder subview right below it. I would like to pin the MKMapView to the top of the screen and allow the placeholder subview to slide over it and cover it up.
Apple says it's now possible to accomplish this with Autolayout but it doesn't seem to be working for me. The code below displays the UIScrollView and it's subviews properly, but the map still scrolls along with everything else. Am I missing something really obvious?
https://developer.apple.com/LIBRARY/ios/technotes/tn2154/_index.html
Note that you can make a subview of the scroll view appear to float (not scroll) over the other scrolling content by creating constraints between the view and a view outside the scroll view’s subtree, such as the scroll view’s superview.
UIView .m file:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Create scroll view and add to view
UIScrollView *scrollView = [[UIScrollView alloc] init];
[scrollView setBackgroundColor: [UIColor clearColor]];
[scrollView setShowsVerticalScrollIndicator:NO];
[scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:scrollView];
// Create map view and add to scroll view
mapView = [[MKMapView alloc] init];
mapView.showsPointsOfInterest = NO;
mapView.translatesAutoresizingMaskIntoConstraints = NO;
[scrollView addSubview:mapView];
// Create a placeholder image to scroll over map view
UIImageView *randomPlaceholderStuff = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"stuff.png"]];
[dividingLine setTranslatesAutoresizingMaskIntoConstraints:NO];
[scrollView addSubview:dividingLine];
// Layouts
NSDictionary *viewArranging = NSDictionaryOfVariableBindings(scrollView, tourMap, randomPlaceholderStuff);
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView]|"
options:0
metrics:0
views:viewArranging]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView]|"
options:0
metrics:0
views:viewArranging]];
UIView *referenceSuperView = scrollView.superview;
[referenceSuperView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[mapView(320)]|"
options:0
metrics:0
views:viewArranging]];
[referenceSuperView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[mapView(320)]"
options:0
metrics:0
views:viewArranging]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-334-[randomPlaceholderStuff]|"
options:0
metrics:0
views:viewArranging]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[randomPlaceholderStuff(320)]|"
options:0
metrics:0
views:viewArranging]];
}
return self;
}
#end
Edit:
jrturton's answer was spot on. Here's the code that ended up working:
[self addConstraint:[NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:scrollView.superview attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:scrollView.superview attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:mapView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:320.0]];
You can't do what you're trying with visual format language. The | character will always be interpreted as the view's direct superview, so you're not creating the constraints you think you are.
You need to use the longer method (constraintWithItem...) to create individual constraints, where item1 is your floating view, and item2 is the scroll view's superview.
You can add floating sub view over UIScrollView by adding/moving subview to super view of scrollview like:
Place/set your button over scroll view (not inside scroll view) as shown here in this snapshot. And also set button constraints (position) with respect to super view of your scrollview.
Here is ref. snapshot of hierarchy of position of each view over each-other.

Aligning a UIImageView with Auto Layout

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 )
|-[

Resources