iOS add view as subview programatically autolayout conflicting constraints - ios

I have the following setup:
2 view controllers defined in storyboard.
I have a custom segue that gets triggered by activating a control on the first vc.
in the -(void)perform method of the segue, I have to add the second vc's view as subview to the first with some animation.
-(void)perform{
MyVC *myVC = self.destinationViewController;
UIViewController *sourceVC = self.sourceViewController;
UIView *myView = myVC.view;
[sourceVC.view addSubview:myView];
NSDictionary *viewDictionary = NSDictionaryOfVariableBindings(sourceVC.view, drawerView);
[myView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[myView(200)]"
options:0 metrics:nil views:viewDictionary]];
}
Unfortunately I get the following error.
2014-10-31 16:57:33.899 ReviewsAgain[19971:5435238] 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:0x7fb0306368c0 H:[UIView:0x7fb0306337e0(200)]>",
"<NSLayoutConstraint:0x7fb030635c80 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7fb0306337e0(375)]>"
)
What I believe is happening here - myView has its constraints applied at build time 'from its own vc`, and when I am to add it as a subview to another view - things get messy and it cannot understand which constrains to apply - the ones applied earlier - or the ones I have defined.
I wonder what is the right way to solve this ?

So in order for this to work the view which is being 'taken' from its own viewController must have its setTranslatesAutoresizingMaskIntoConstraints set to NO.
[myView setTranslatesAutoresizingMaskIntoConstraints:NO];

Related

Getting "Unable to simultaneously satisfy constraints" after updating constraint in iOS 7

I've got a UITableViewController, with a custom view, with some subviews and controls inside, in the header of the table. Each one of these subviews are a custom subview (use a custom subview to draw corner radius with IBInspectable) and each have constraints for top, bottom, leading and trailing space (all set to 8) as well as for height (set to 60, 80 or 100, depending of each subview).
One of these subviews constraints can be modified programmatically in run time depending of user interaction. To do that, I create this two methods:
- (void)showSearchTypeTermField:(BOOL)animated
{
self.searchTypeHeightConstraint.identifier = #"Height 100";
[self.searchTypeHeightConstraint setConstant:100.0];
if (animated) {
[self.tableView.tableHeaderView layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
[self.tableView.tableHeaderView layoutIfNeeded];
}];
} else {
[self.tableView.tableHeaderView layoutIfNeeded];
}
self.searchTypeTermField.hidden = NO;
self.searchTypeTermField.text = #"";
[self.tableView.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingExpandedSize];
}
- (void)hideSearchTypeTermField:(BOOL)animated
{
self.searchTypeHeightConstraint.identifier = #"Height 60";
[self.tableView.tableHeaderView setAutoresizingMask:UIViewAutoresizingFlexibleHeight];
[self.searchTypeHeightConstraint setConstant:60.0];
if (animated) {
[self.tableView.tableHeaderView layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
[self.tableView.tableHeaderView layoutIfNeeded];
}];
} else {
[self.tableView.tableHeaderView layoutIfNeeded];
}
self.searchTypeTermField.hidden = YES;
[self.tableView.tableHeaderView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
}
Because all of these subviews are in the table header view, every time I change the constraint, header should expand or compress depending of the case, thats why I use self.tableView.tableHeaderView systemLayoutSizeFittingSize.
The problem is I'm getting this error, when I call [self hideSearchTypeTermField:NO] the first time on viewdidLoad despite that visually everything seems fine:
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:0x7bf504e0 V:[SeachFormBackgroundView:0x7bea3ab0(60)]>",
"<NSLayoutConstraint:0x7bf51760 V:[SeachFormBackgroundView:0x7bf50690(60)]>",
"<NSLayoutConstraint:0x7bf53390 'Height 60' V:[SeachFormBackgroundView:0x7bf518e0(60)]>",
"<NSLayoutConstraint:0x7bf53ec0 V:[SeachFormBackgroundView:0x7bf535a0(80)]>",
"<NSLayoutConstraint:0x7bf54a80 V:[SeachFormBackgroundView:0x7bf54180(80)]>",
"<NSLayoutConstraint:0x7bf54eb0 V:|-(8)-[SeachFormBackgroundView:0x7bea3ab0] (Names: '|':UIView:0x7bea3a10 )>",
"<NSLayoutConstraint:0x7bf54f40 V:[SeachFormBackgroundView:0x7bea3ab0]-(8)-[SeachFormBackgroundView:0x7bf50690]>",
"<NSLayoutConstraint:0x7bf54f70 V:[SeachFormBackgroundView:0x7bf50690]-(8)-[SeachFormBackgroundView:0x7bf518e0]>",
"<NSLayoutConstraint:0x7bf55090 V:[SeachFormBackgroundView:0x7bf518e0]-(8)-[SeachFormBackgroundView:0x7bf535a0]>",
"<NSLayoutConstraint:0x7bf550f0 V:[SeachFormBackgroundView:0x7bf54180]-(8)-| (Names: '|':UIView:0x7bea3a10 )>",
"<NSLayoutConstraint:0x7bf55180 V:[SeachFormBackgroundView:0x7bf535a0]-(8)-[SeachFormBackgroundView:0x7bf54180]>",
"<NSAutoresizingMaskLayoutConstraint:0x7c149fc0 h=--& v=--& V:[UIView:0x7bea3a10(428)]>"
)
I'm really lost with this issue. What I'm doing wrong?
Thanks.
If you try adding the heights for the SeachFormBackgroundView instances (60 + 60 + 60 + 80 + 80 + 8 * 6 = 388 and their separators you'll see that they aren't equal to the parent UIView height (428). That's why you get this message.
You have to either adjust the constraints so that they add up to the parent view height, or resize the parent so that it's size matches the child constraints.
You don't need all of the constraints you have right now. Since you're setting a specific height to all of the subviews and spacing constraints, you only need to anchor them to the top or to the bottom of the superview.
Edit:
You don't need both of these constraints: V:|-(8)-[SeachFormBackgroundView:0x7bea3ab0] and V:[SeachFormBackgroundView:0x7bf54180]-(8)-|. The first one anchors your subviews to the top of the superview, and the scond one anchors them to the bottom of your superview. Remove one of them (I'd expect the bottom one) and the views will fall in place without throwing any AutoLayout exceptions.
When you set the header view the table view reads its size and sets a constraint for that height. Updating the constraints on the header view itself don't cause a reevaluation of that height. You should remove the header view, update its constraints, lay it out and then add the header view again.

Dynamic UIScrollView content: Unable to simultaneously satisfy constraints

I'm attempting to set up autolayout for a paging scroll view with a dynamic number of pages (one primary subview per page). My view hierarchy is set up as follows:
Main view
Scroll view
UIView (fits content)
TutorialSubview
...
After adding all the views to an array, I have the following code to dynamically generate constraints:
self.iphoneSVContentWConstr.constant = (self.subVWidth * self.contentSV.frame.size.width);
NSMutableDictionary *views = [NSMutableDictionary new];
NSLayoutFormatOptions formatForVert = (NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom);
NSMutableString *format = [NSMutableString stringWithString:#"|"];
int idx = 0; for (UIView *v in self.viewArr) {
NSString *key = [NSString stringWithFormat:#"View%i", idx];
[views setObject:v forKey:key];
[format appendFormat:#"[%#(%.f)]", key, self.subVWidth];
idx++;
}
[format appendString:#"|"];
NSLog(#"%#", format);
//Update the content view
[self.iphoneSVContent addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:format options:formatForVert metrics:nil views:views]];
[self.contentSV layoutIfNeeded];
This ends up outputting:
|[View0(320)][View1(320)][View2(320)][View3(320)]|
Which seems correct. However, I'm getting the following errors thrown:
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:0x17409f1d0 H:[UIView:0x174197c40(102400)]>",
"<NSLayoutConstraint:0x1742811d0 H:|-(0)-[TutorialSubview:0x1743a1960] (Names: '|':UIView:0x174197c40 )>",
"<NSLayoutConstraint:0x170287490 H:[TutorialSubview:0x1743a1960(320)]>",
"<NSLayoutConstraint:0x1702873f0 H:[TutorialSubview:0x1743a1960]-(0)-[TutorialSubview:0x1743a3100]>",
"<NSLayoutConstraint:0x1702871c0 H:[TutorialSubview:0x1743a3100(320)]>",
"<NSLayoutConstraint:0x1702872b0 H:[TutorialSubview:0x1743a3100]-(0)-[TutorialSubview:0x1743a3480]>",
"<NSLayoutConstraint:0x170287210 H:[TutorialSubview:0x1743a3480(320)]>",
"<NSLayoutConstraint:0x170286fe0 H:[TutorialSubview:0x1743a3480]-(0)-[TutorialSubview:0x1703a31e0]>",
"<NSLayoutConstraint:0x170287080 H:[TutorialSubview:0x1703a31e0(320)]>",
"<NSLayoutConstraint:0x170286f40 H:[TutorialSubview:0x1703a31e0]-(0)-| (Names: '|':UIView:0x174197c40 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x170286fe0 H:[TutorialSubview:0x1743a3480]-(0)-[TutorialSubview:0x1703a31e0]>
I have the following constraints set up on self.iphoneSVContent (the view they're all being added to within the scroll view):
At this point, I'm just unsure as to what's causing the issue. Any insight is much appreciated!
The problem seems to be the first constraint you are changing the constant
self.iphoneSVContentWConstr.constant = (self.subVWidth * self.contentSV.frame.size.width);
The value for the view's width you set with this constraint (which is 102400, from the conflicting constraints output) is not the same as 4* 320 (which is the width of the views inside it).
If the width of the UIView inside the scrollview is defined completely by the number of subviews it has inside (which already have defined width), you should not need this constraint self.iphoneSVContentWConstr, because you have enough constraints for the width to be calculated unambiguously.
If you still want to keep this width constraint, make sure you set the constant to the correct value, with something like:
self.iphoneSVContentWConstr.constant = (self.subVWidth * self.viewArr.count);
I suppose the width/height of the scrollview you set in the storyboard. That should be enough for it to work and for the constraints to not crash anymore.
I really liked the way you constructed the visual format string! Cool :)
Let me know how it went. Good luck!

iOS Autolayout issue, Adding login view looks bad (image inside)

I'm adding a login view in my tableview. But as you can see on the image below it doesn't look great.
I am using auto layout for both the tableview and the loginview.
For the loginview I have the constraints showed on the image below, but what's strange(to me) is that these constraints seem to be valid for both the Green View and its superview (the black). Because, when I change it for one of them, it changes for the other as well.
Note: The loginview has separate a controller, but the controller is not presented. The loginview is just added to the tableview.
Any suggestions?
From what I can see in this image, there are 4(!) views you need to account for.
1.) the superview
2.) the UITableview
3.) the black view
4.) the login-view (blue-green?)
It looks like the constraints for (4) are set correctly in relation to (3)
To me, I'd examine the constraints for (3) in relation to (1).
In other words make sure the leading/trailing top/bottom constraints are all 0 or to margin in relation to the superview, and you should be good to go.
Here is a quick example on how you could implement this. This assumes you are using view controller containment, which you should be.
child is your signIn view and self is the Table view controller
// Add login view to my view
[self addChildViewController:child];
child.view.frame = self.view.bounds;
[self.view addSubview:child.view];
[child didMoveToParentViewController:self];
// Add constraints
NSDictionary * views = #{#"view" : child.view, #"super" : self.view};
NSArray * hConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[view]-|" options:0 metrics:nil views:views];
NSArray * vConstraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[view]-|" options:0 metrics:nil views:views];
[self.view addConstraints: [hConstraints arrayByAddingObjectsFromArray:vConstraints]];

Unable to simultaneously satisfy constraints in case of UICollectionViewCell

I am using iCarousel to implement a cover flow. I provide views to it in my iCarousel data source method, like this:
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSInteger)index reusingView:(UIView *)view
{
if(!view) {
view = [[SDHotOnThumbView alloc]initWithFrame:CGRectMake(0, 0, kHotOnThumbViewWidth, kHotOnThumbViewHeight)];
view.contentMode = UIViewContentModeScaleAspectFit;
}
[((SDHotOnThumbView*)view) setData:[self.dataArray objectAtIndex:index]];
return view;
}
The method setData apart from setting the label's text in SDHotOnThumbView instance, and starting image download for the image view in it, also makes a call to a method updateLabelLayoutWithAttributedText, which is implemented as under in the SDHotOnThumbView class:
-(void)updateLabelLayoutWithAttributedText:(NSMutableAttributedString*) labelString
{
float height = [self heightOfAttributedText:labelString width:self.frame.size.width];
self.descriptionLabel.translatesAutoresizingMaskIntoConstraints = NO;
//self.descriptionLabel.preferredMaxLayoutWidth = self.frame.size.width;
NSDictionary *viewsDict = #{#"desclabel":self.descriptionLabel};
NSArray *constraintsH = [NSLayoutConstraint constraintsWithVisualFormat:#"H:|-0- [desclabel]-0-|" options:0 metrics:nil views:viewsDict];
[self addConstraints:constraintsH];
NSArray *constraintsV = [NSLayoutConstraint constraintsWithVisualFormat:[NSString stringWithFormat:#"V:[desclabel(==%f)]-10-|", height]
options:0
metrics:nil
views:viewsDict];
[self addConstraints:constraintsV];
}
iCarousel recycles views, and I have to allocate a view if the recycled view is nil, and configure it every time the carousel asks for a view at a given index. Based on the size of an attributed string, I update vertical constraints for the label with the new height, in the second method I have attached, so that the label is resized as per the data it contains. The views layout just fine, except that I get . For example, I get
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:0x7b82f330 V:[UILabel:0x7d861820'SanDisk Cruzer Blade USB ...'(51.2)]>",
"<NSLayoutConstraint:0x7d862df0 V:[UILabel:0x7d861820'SanDisk Cruzer Blade USB ...'(65.6)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7d862df0 V:[UILabel:0x7d861820'SanDisk Cruzer Blade USB ...'(65.6)]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in
<UIKit/UIView.h> may also be helpful.
Logging computed heights for the label, I can see that probably when the cell is removed from the collection view and re-added for some different index in the collection view, it probably holds on to its old height, and tries to add a new vertical constraint, breaking the previous one. I even tried removing all label constraints from the view, and adding everytime set data is called, but i see the same happening in Xcode, even though everything seems to work as expected. How do I go about these Xcode warnings, or is it fine if the layout is as desired.
Somehow you doubly inserted 2 different heights to the label, the first one's height is 51.2 and the second one is 65.6. Please check your code once more.

How to properly constrain UITableView using Auto Layout

I want to place a UITableView such that it takes up the entirety of one of my view controller's subviews. Here's the view hierarchy, where self is my UIViewController subclass:
[self.view] -> [self.rootView] -> [self.tableView]
I know that I need to set setTranslatesAutoresizingMaskIntoConstraints to NO for any view whose subviews I want to lay out with Auto Layout, so here's my viewDidLoad.
[super viewDidLoad];
self.rootView = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, 320.0, 568.0)];
[self.rootView setTranslatesAutoresizingMaskIntoConstraints:NO];
So far so good. Now I want to add a UITableView.
self.tableView = [[UITableView alloc]init]; // same thing with initWithFrame:CGRectZero
[self.rootView addSubview:self.tableView];
No problem so far, but I can't see the table view (because it doesn't have a frame). Let's add some constraints:
[self.rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|-0-[table]-0-|" options:0 metrics:nil views:#{#"table": self.tableView}]];
[self.rootView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-0-[table]-0-|" options:0 metrics:nil views:#{#"table": self.tableView}]];
Everything breaks. self.rootView.backgroundColor is green and the cells are blue and full of text, but there's nothing on the screen but grey, maybe indicating that self.view is no longer in the view hierarchy? I've done a bunch of googling but can't find a real solution anywhere. I've also tried adding all the constraints one at a time the other way and the same thing happens. Does anyone have any ideas?
I believe you should be calling setTranslatesAutoresizingMaskIntoConstraints:NO on your UITableView instead of on self.rootView. But as rdelmar commented, first make sure your table view is not what you are seeing with the grey background since it will fill the screen and cover the other views.
And another note, I believeself.rootView = [[UIViewController alloc]initWithFrame...] above actually should be [[UIView alloc] initWithFrame...] since controllers don't have frames but maybe it's just incorrect above as this would cause an error when trying to run.

Resources