How to do AutoLayout programmatically with Horizontal scrolling? - ios

I'm trying to do Horizontal scrolling on iOS using AutoLayout programmatically. Here's the link to my github I'm trying to add another NewsSection to the next page but I'm not sure how to do it. Here's the code I'm working on.
- (void) setupNewsView
{
UIView *newsView = [[NewsSection alloc] initWithFrame:CGRectZero];
newsView.backgroundColor = [UIColor redColor];
UIView *anotherNewsView = [[NewsSection alloc] initWithFrame:CGRectZero];
anotherNewsView.backgroundColor = [UIColor blueColor];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
newsView.translatesAutoresizingMaskIntoConstraints = NO;
anotherNewsView.translatesAutoresizingMaskIntoConstraints = NO;
self.scrollView.pagingEnabled = YES;
[self.scrollView addSubview:newsView];
[self.scrollView addSubview:anotherNewsView];
NSDictionary *viewsDict = #{ #"newsView": newsView, #"anotherNewsView": anotherNewsView };
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[newsView]|"
options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom
metrics:nil
views:viewsDict]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[newsView]|"
options:0
metrics:nil
views:viewsDict]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:newsView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeWidth
multiplier:1.0f
constant:0.0f]];
[self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:newsView
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeHeight
multiplier:1.0f
constant:0.0f]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[anotherNewsView(200)]|" options:0 metrics:nil views:viewsDict]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[anotherNewsView(100)]|" options:0 metrics:nil views:viewsDict]];
[self.scrollView setContentSize:CGSizeMake(self.scrollView.frame.size.width * 2, self.scrollView.frame.size.height)];
Right now the app looks like this. What I want is user should be able to scroll to the right and see the blue screen. What constraint do I need to add?

The constraints you have set the blue view to fill the scroll view AND to be of a fixed width, which causes a conflict. The |s at either end of the constraint string make anotherNewsView hug the bounds of its superview (scrollView).
Try dropping the final |s from your constraints. Also position anotherNewsView to be left-aligned with newsView rather than scrollView.
These constraints should do the trick:
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[newsView(500)]" options:0 metrics:nil views:viewsDict]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[newsView(500)][anotherNewsView(100)]" options:0 metrics:nil views:viewsDict]];
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[anotherNewsView(200)]" options:0 metrics:nil views:viewsDict]];

Related

How to make Vertical spacing B/W UIFields using Auto-layouts in Ios

Hi I am very new for iOS and in my app I am using Auto-layouts Visual format
But how can I change vertical spacing at various conditions
For this I used below code but vertical spacing is not setting b/w UIfields
see my below screen vertical space I have given here "15" but it's not applying
my code:-
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary * views1 = NSDictionaryOfVariableBindings(RoundTripLabel,RoundTripDateTextField,RoundTripButton,BottomLabel1,BottomLabel2);
//Appying Horizental contstraints:-
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[RoundTripLabel]-8-|" options:0 metrics:nil views:views1]];
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[RoundTripDateTextField]-8-[RoundTripButton(30)]-|" options:0 metrics:nil views:views1]];
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[BottomLabel1]-8-|" options:0 metrics:nil views:views1]];
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[BottomLabel2]-8-|" options:0 metrics:nil views:views1]];
//Appying Vertical contstraints:-
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-100-[RoundTripLabel(17)]-10-[RoundTripDateTextField]-0-[BottomLabel1(17)]-10-[BottomLabel2(17)]" options:0 metrics:nil views:views1]];
NSDictionary * views2 = NSDictionaryOfVariableBindings(RoundTripLabel,RoundTripButton);
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[RoundTripLabel]-10-[RoundTripButton]" options:0 metrics:nil views:views2]];
//Defining Heights Using Constraint With Item Formate:-
RoundTripDateTextFieldHeight = [NSLayoutConstraint constraintWithItem:RoundTripDateTextField attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:30.0f];
[MainScrollView addConstraint:RoundTripDateTextFieldHeight];
RoundTripButtonHeight = [NSLayoutConstraint constraintWithItem:RoundTripButton attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:30.0f];
[MainScrollView addConstraint:RoundTripButtonHeight];
VerticalSpacing = [NSLayoutConstraint constraintWithItem:BottomLabel1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:RoundTripDateTextField attribute:NSLayoutAttributeBottom multiplier:1.0f constant:15.0f];
[MainScrollView addConstraint:RoundTripDateTextFieldHeight];
[MainScrollView setNeedsDisplay];
}
In your code, there is problem with these constraints
1st contsraint:
instead of adding RoundTripDateTextFieldHeight again, you need to add VerticalSpacing
VerticalSpacing = [NSLayoutConstraint constraintWithItem:BottomLabel1 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:RoundTripDateTextField attribute:NSLayoutAttributeBottom multiplier:1.0f constant:15.0f];
[MainScrollView addConstraint:VerticalSpacing]; // instead of [MainScrollView addConstraint:RoundTripDateTextFieldHeight];
2nd
add vertical spacing between 2 labels
NSLayoutConstraint *VerticalSpacingLbl2 = [NSLayoutConstraint constraintWithItem:BottomLabel2 attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:BottomLabel1 attribute:NSLayoutAttributeBottom multiplier:1.0f constant:10.0f];
[MainScrollView addConstraint:VerticalSpacingLbl2];
3rd
change below constraint
//Appying Vertical contstraints:-
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-100-[RoundTripLabel(17)]-10-[RoundTripDateTextField]-0-[BottomLabel1(17)]-10-[BottomLabel2(17)]" options:0 metrics:nil views:views1]];
with this
[MainScrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-100-[RoundTripLabel(17)]-10-[RoundTripDateTextField]" options:0 metrics:nil views:views1]];

How to restrict UIButton height using NSLayoutConstraint

I have UItoolbar in which added two items 1. UITextView and 2. UIButton.
The Property of UITextview is to autogrowing based on entered text.
While growing of height of UITextview i want to restrict UIButton height, i.e UIButton height should not be increase.
[_toolbar setItems:[NSArray arrayWithObjects:barButtonItemCommentText,barButtonItemSubmit,nil]];
submitBtn.translatesAutoresizingMaskIntoConstraints = NO;
textView.translatesAutoresizingMaskIntoConstraints = NO;
_toolbar.translatesAutoresizingMaskIntoConstraints = NO;
[_toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-8-[textView]-79-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]];
[_toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-8-[textView]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(textView)]];
[_toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[submitBtn]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(submitBtn)]];
[_toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-8-[submitBtn]-8-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(submitBtn)]];
[_toolbar addConstraint:[NSLayoutConstraint constraintWithItem:submitBtn
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:nil
attribute:NSLayoutAttributeNotAnAttribute
multiplier:1.0
constant:60]];
[textView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[textView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[_toolbar setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[_toolbar addConstraint:[NSLayoutConstraint constraintWithItem:_toolbar attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationLessThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:MaxToolbarHeight]];
For reference , see the attached screen-shot:
The below mentioned constraint is making it to stretch in vertical direction as it ask to be always 8 pixels from top and bottom.
[_toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-8-[submitBtn]-8-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(submitBtn)]];
If you change it to constant height than it will not grow vertically, in this case always a height of 24.
[_toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[submitBtn(24)]-8-|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(submitBtn)]];

scrollView: how to create a pure auto layout scrollview with paging?

Can someone tell me what I did wrong here...
- pin the UIScrollView to its container view
- pin all subviews onto UIScrollView
After reading the Apple TechNote about it, I tried both hybrid method and pure auto layout method. The hybrid method using NIB works awful with paging, it looks like a big picture in a scrollview, rather than paged.
I then created the pure auto layout version in code, UIScrollView as subview of a UIView. This time the view stuck, and the UIImage is gigantic, like its full size:
//scroll view
if (self.scrollView == nil)
{
self.scrollView = [[UIScrollView alloc] initWithFrame:self.frame];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView setClipsToBounds:NO];
[self.scrollView setPagingEnabled:YES];
[self addSubview: self.scrollView];
[self addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[scrollView(300)]|"
options:0 metrics:nil
views:#{#"scrollView":self.scrollView}]];
[self addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[scrollView(300)]|"
options:0 metrics:nil
views:#{#"scrollView":self.scrollView}]];
-(void) createContentView
{
for (int i=0; i<self.pageImages.count; i++) {
UILabel* topLabel = [[UILabel alloc] init];
topLabel.text = [NSString stringWithFormat:#"topLabel %d", i+1];
[topLabel sizeToFit];
[self.topLabelArray insertObject:topLabel atIndex:i];
[self.scrollView addSubview:topLabel];
topLabel.translatesAutoresizingMaskIntoConstraints = NO;
UILabel* bottomLabel = [[UILabel alloc] init];
bottomLabel.text = [NSString stringWithFormat:#"bottomLabel %d", i+1];
[bottomLabel sizeToFit];
[self.bottomLabelArray insertObject:bottomLabel atIndex:i];
[self.scrollView addSubview:bottomLabel];
bottomLabel.translatesAutoresizingMaskIntoConstraints = NO;
UIButton* button = [[UIButton alloc] init];
button.titleLabel.text = [NSString stringWithFormat:#"button %d", i+1];
[button sizeToFit];
[self.buttonArray insertObject:button atIndex:i];
[self.scrollView addSubview:button];
button.translatesAutoresizingMaskIntoConstraints = NO;
UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[self.pageImages objectAtIndex:i]]];
imageView.frame = CGRectMake(0,0,200,200);
imageView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.contentMode = UIViewContentModeScaleAspectFit;
[self.pageViews insertObject:imageView atIndex:i];
[self.scrollView addSubview:imageView];
NSDictionary* viewsDictionary = #{#"topLabel":topLabel,
#"bottomLabel":bottomLabel,
#"button": button,
#"imageView": imageView
};
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(10)-[topLabel]-(10)-[imageView]-(10)-[bottomLabel]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[button]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
if (i==0)
{
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(10)-[topLabel]"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(10)-[imageView]"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-(10)-[bottomLabel]-(10)-[button]"
options:0 metrics:nil
views:viewsDictionary]];
}
else if (i == self.pageImages.count)
{
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:-(10)-[topLabel]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:-(10)-[imageView]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:-(10)-[bottomLabel]-(10)-[button]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
}
else
{
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[prevTopLabel]-(10)-[topLabel]"
options:0 metrics:nil
views:#{#"prevTopLabel": [self.topLabelArray objectAtIndex: i-1],
#"topLabel": topLabel
}]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[prevImageView]-(10)-[imageView]"
options:0 metrics:nil
views:#{#"prevImageView": [self.pageViews objectAtIndex: i-1],
#"imageView": imageView
}]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[prevButton]-(10)-[bottomLabel]-(10)-[button]"
options:0 metrics:nil
views:#{#"prevButton": [self.buttonArray objectAtIndex: i-1],
#"button":button,
#"bottomLabel": bottomLabel
}]];
}
[self.scrollView addConstraint:[NSLayoutConstraint
constraintWithItem:topLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:imageView
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0.0]];
[self.scrollView addConstraint:[NSLayoutConstraint
constraintWithItem:bottomLabel
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:imageView
attribute:NSLayoutAttributeCenterX
multiplier:0.8
constant:0.0]];
// [self.scrollView addConstraint:[NSLayoutConstraint
//
Ok, there's a few things wrong here:
else if (i == self.pageImages.count)
This clause will never run, as your loop count is set to break when i == self.pageImages.count -1, here for (int i=0; i<self.pageImages.count; i++).
Next
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:-(10)-[topLabel]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:-(10)-[imageView]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
[self.scrollView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:-(10)-[bottomLabel]-(10)-[button]-(10)-|"
options:0 metrics:nil
views:viewsDictionary]];
These will also produce invalid constraints, specifically #"H:-(10)-[bottomLabel].... is not pinning the furthest left constraint to anything. All visual format strings that connect views need to start or finish with a view. Either a subview ([subview]) or a superview (|). To fix this, you need to keep a reference to the previous pages label in the loop, and add that to the beginning of your VFL string. Something like this
#"H:[prevBottomLabel]-(10)-[bottomLabel]....
prevBottomLabel = bottomLabel;
// Continue loop
Next:
UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[self.pageImages objectAtIndex:i]]];
imageView.frame = CGRectMake(0,0,200,200);
imageView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.contentMode = UIViewContentModeScaleAspectFit;
This will not have the desired effect, as you can't manually set the frame, then switch on autolayout. Currently, the frame value is ignored, and the height and width are set by the intrinsic content size of the image view, which will be the height and width of the image. If you want to set the height and width, you need to do it with constraints, as so:
[imageView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"H:[imageView(200)]"
options:0 metrics:nil
views:viewsDictionary]];
[imageView addConstraints:
[NSLayoutConstraint constraintsWithVisualFormat:#"V:[imageView(200)]"
options:0 metrics:nil
views:viewsDictionary]];
That should give you some pointers, there may be other mistakes in there too but fixing these is a good place to start.

Center views using *only* visual format?

I'm trying to center 2 views with different height vertically.
UILabel *view1 = [[UILabel alloc] init];
view1.backgroundColor = [UIColor redColor];
view1.text = #"view1\nline2";
view1.textAlignment = NSTextAlignmentCenter;
view1.numberOfLines = 0;
UILabel *view2 = [[UILabel alloc] init];
view2.backgroundColor = [UIColor greenColor];
view2.text = #"view2\nline2";
view2.textAlignment = NSTextAlignmentCenter;
view2.numberOfLines = 0;
[self.view addSubview:view1];
[self.view addSubview:view2];
NSDictionary *views = #{#"view1": view1, #"view2" : view2};
[self.view setTranslatesAutoresizingMaskIntoConstraints:NO];
for (UIView *view in views.allValues) {
[view setTranslatesAutoresizingMaskIntoConstraints:NO];
}
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view1]-[view2(==view1)]-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=20)-[view1(200)]-(>=20)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(>=20)-[view2(100)]-(>=20)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:nil views:views]];
This manages to make the centers of both aligned vertically, but they are at the bottom of the superview!
I want to center vertically not only relative to each other, also in the superview.
I added a constraint like this and it works:
[self.view addConstraint:
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:self.view
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0]];
But I want to know, is this possible to achieve using only with visual format?
Yes, it's possible, but only by adding spacer views. So if you create a couple of views, lets call them spacer1 and spacer2, you can center view1 with this string,
#"V:|[spacer1][view1][spacer2(==spacer1)]|"
This assumes that view1 has a fixed height. I wouldn't recommend doing it this way though, I would just use the code you show at the bottom of your post.
What about calculating the Y in "metrics" ?
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-centerY-[view1(200)]-(>=20)-|"
options:NSLayoutFormatAlignAllCenterY
metrics:#{#"centerY": #(CGRectGetHeight(self.view.frame)/2.0 - 100)}
views:views]];

Autolayout "conflicting constraints"

I've created a custom UIView that I use modally to display a UIToolbar and UIPickerView. I'm trying to make it very reusable, so I'm creating all of the UI in code, including setting constraints.
Here's the method body I use to set the view up to be added to another view, and then to animate the controls up onto the view from the bottom.
My view hierarchy is as follows:
"Owner" view (view to which this view is added):<br>
|-->"Background" view (set to the full size of "Owner", but mainly used as a dimmed background)<br>
|-->"Container" view (view which holds the toolbar and picker)
|--> Toolbar
|--> Picker
Here's the code I use to set up the UI:
- (void)prepareForView:(UIView *)view {
UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
self.containerView = containerView;
UIPickerView *picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 162.0f)];
[picker setTranslatesAutoresizingMaskIntoConstraints:NO];
self.picker = picker;
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 44.0f)];
[toolbar setTranslatesAutoresizingMaskIntoConstraints:NO];
UIBarButtonItem *done = ...;
UIBarButtonItem *flexSpace = ...;
UIBarButtonItem *cancel = ...;
toolbar.items = #[done, flexSpace, cancel];
[containerView addSubview:picker];
[containerView addSubview:toolbar];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[picker]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(picker)]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[toolbar]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(toolbar)]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[toolbar(==44)][picker(==162)]|" options:NSLayoutFormatAlignAllLeading metrics:nil views:NSDictionaryOfVariableBindings(toolbar, picker)]];
[containerView layoutIfNeeded];
[self addSubview:containerView];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[containerView]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(containerView)]];
self.containerTop = [NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0f constant:self.frame.size.height];
[self addConstraint:self.containerTop];
[self layoutIfNeeded];
}
Basically, I want containerView to be sized according to its contents (which should be a static 206 points). Then, I set a vertical space constraint between its top and the top of its superview (which is self). Later, I animate changing that so that the toolbar and picker "slide up" onto the screen.
Here's the animation code (the error is always triggered BEFORE this point):
// Add the view as a subview
[view addSubview:self];
// Setup view for display (here's what triggers the message)
[self prepareForView:view];
// Animate into view
[UIView animateWithDuration:animated?0.4f:0.0f
animations:^{
self.alpha = 1.0f;
}
completion:^(BOOL finished) {
// Now, slide the container view in from the bottom of the screen
self.containerTop.constant = self.frame.size.height - self.containerView.frame.size.height;
[UIView animateWithDuration:animated?0.4f:0.0f
animations:^{
[self layoutIfNeeded];
}
completion:^(BOOL finished) {
if (postDisplay != nil) {
postDisplay();
}
}
];
}
];
This is currently displaying correctly on all simulators and devices in testing, but I hate having warnings/errors, and I'm worried that this might NOT work as desired at some point.
Here's the actual error message that's displayed (I'm pretty sure this is exactly the same error every time):
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSLayoutConstraint:0x170283430 V:|-(0)-[UIToolbar:0x137590850] (Names: '|':UIView:0x170382b10 )>",
"<NSLayoutConstraint:0x170283480 V:[UIToolbar:0x137590850(44)]>",
"<NSLayoutConstraint:0x174084ba0 V:[UIToolbar:0x137590850]-(0)-[UIPickerView:0x137586100]>",
"<NSLayoutConstraint:0x174081fe0 V:[UIPickerView:0x137586100(162)]>",
"<NSLayoutConstraint:0x17409bbc0 V:[UIPickerView:0x137586100]-(0)-| (Names: '|':UIView:0x170382b10 )>",
"<NSLayoutConstraint:0x174095b80 V:[UIView:0x170382b10(736)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x174084ba0 V:[UIToolbar:0x137590850]-(0)-[UIPickerView:0x137586100]>
What I don't understand is that all of the constraints ARE expected, and then the displayed UI is what I intend. When I inspect the view's layout with po [self.containerView recursiveDescription] in the debugger, I see the frames being exactly what I think they ought to be. Where am I going wrong?
The "Container" view appears to have a height of 736 from the line below:
<NSLayoutConstraint:0x174095b80 V:[UIView:0x170382b10(736)]>
You have laid out the view as below:
------ Top of UIView ------- (y = 0)
- 0 Space -
UIToolBar (y = 0 to y = 44)
- 0 Space -
UIPickerView (y = 44 to y = 206) ***
- 0 Space -
------ Bottom of UIView ----- (y = 736)
*** This is where the conflict arrises. This can't be 0 space away from the bottom of the UIView with all of the other constraints set the way that they are or the height of the UIView cannot be what it is with all of the other constraints.
EDIT:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[containerView]|" options:NSLayoutFormatAlignAllBaseline metrics:nil views:NSDictionaryOfVariableBindings(containerView)]];
When you use "|" within a the visualFormat for a constraint this is the parent view. Within this line you are saying you want the parent view to 0 space to the top of the [containerView] and also 0 space from the bottom of the [containerView]. This makes the container view the same height as the parent.
Edit #2:
Look at the following. I believe this is close to what you are trying to accomplish.
[containerView addSubview:picker];
[containerView addSubview:toolbar];
[self addSubview:containerView];
// Height & Width for containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[containerView(%f)]", containerView.frame.size.height] options:nil metrics:nil views:#{#"containerView":containerView}]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:[containerView(%f)]", containerView.frame.size.width] options:nil metrics:nil views:#{#"containerView":containerView}]];
// Height & Width for picker
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[picker(%f)]", picker.frame.size.height] options:nil metrics:nil views:#{#"picker":picker}]];
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:[picker(%f)]", picker.frame.size.width] options:nil metrics:nil views:#{#"picker":picker}]];
// Height & Width for toolbar
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[toolbar(%f)]", toolbar.frame.size.height] options:nil metrics:nil views:#{#"toolbar":toolbar}]];
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"H:[toolbar(%f)]", toolbar.frame.size.width] options:nil metrics:nil views:#{#"toolbar":toolbar}]];
// Vertical Positioning of picker & toolbar in containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[picker]" options:nil metrics:nil views:#{#"picker":picker}]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[toolbar]-0-|" options:nil metrics:nil views:#{#"toolbar":toolbar}]];
// Horizontal Positioning of picker & toolbar in containerView
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:picker attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:toolbar attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
// Center containerView (X/Y) in parent (self)
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
Edit #3: Using 'views' and 'metrics' dictionaries.
NSDictionary *views = #{#"containerView":containerView, #"picker":picker, #"toolbar":toolbar};
NSDictionary *metrics = #{#"hCV":containerView.frame.size.height,
#"wCV":containerView.frame.size.width,
#"hP":picker.frame.size.height,
#"wP":picker.frame.size.width,
#"hT":toolbar.frame.size.height,
#"wT":toolbar.frame.size.width};
[containerView addSubview:picker];
[containerView addSubview:toolbar];
[self addSubview:containerView];
// Height & Width for containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[containerView(hCV)]" options:0 metrics:metrics views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[containerView(wCV)]" options:0 metrics:metrics views:views]];
// Height & Width for picker
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[picker(hP)]" options:0 metrics:metrics views:views]];
[picker addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[picker(wP)]" options:0 metrics:metrics views:views]];
// Height & Width for toolbar
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[toolbar(hT)]" options:0 metrics:metrics views:views]];
[toolbar addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:[toolbar(wT)]" options:0 metrics:metrics views:views]];
// Vertical Positioning of picker & toolbar in containerView
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[picker]" options:0 metrics:nil views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[toolbar]-0-|" options:0 metrics:nil views:views]];
// Horizontal Positioning of picker & toolbar in containerView
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:picker attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[containerView addConstraint:[NSLayoutConstraint constraintWithItem:containerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:toolbar attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
// Center containerView (X/Y) in parent (self)
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0.0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:containerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];

Resources