NSLayoutConstraint and determining which subview is wider - ios

I create a UISwitch and a UILabel in a subclassed container UIView:
UISwitch *toggleSwitch = [UISwitch new];
toggleSwitch.translatesAutoresizingMaskIntoConstraints = FALSE;
[toggleSwitch addTarget:self action:#selector(switchToggleDetected:) forControlEvents:UIControlEventValueChanged];
self.toggleSwitch = toggleSwitch;
[self addSubview:toggleSwitch];
UILabel *label = [UILabel new];
label.translatesAutoresizingMaskIntoConstraints = FALSE;
label.textAlignment = NSTextAlignmentCenter;
label.textColor = [UIColor whiteColor];
label.font = [UIFont fontWithName:HELVETICA_FONT_STYLE_BOLD size:10.0];
label.text = [self.text uppercaseString];
self.label = label;
[self addSubview:label];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[toggleSwitch]-5-[label]" options:NSLayoutFormatAlignAllCenterX metrics:nil views:NSDictionaryOfVariableBindings(toggleSwitch, label)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[toggleSwitch]|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(toggleSwitch, label)]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeRight multiplier:1 constant:0]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:label attribute:NSLayoutAttributeBottom multiplier:1 constant:0]];
which gives me this:
The last two constraints lets me give the container view an "intrinsic size". The container would be rect(0,0,0,0). I then tell the right side should be the same width as the UILabel and the bottom should be the same value as the UILabel to give a height.
The problem I may run is when the label is shorter then the switch:
This will lead to an issue with the switch container view not being placed correct:
[contentImageView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-5-[toggleView]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(toggleView)]];
So I would like to base the constraints on the longer of the two, either the switch or the label but I'm not sure how to determine which is wider. The switch is a constant width, but I can not get the width of the label until after is added to screen which would be too late.
I've tried adding [self layoutIfNeeded] and [label layoutIfNeeded]:
[self layoutIfNeeded];
[label layoutIfNeeded];
DLog(#"label: %#", label);
DLog(#"switch: %#", toggleSwitch);
//Constraints added here
result:
DEBUG | -[SwitchContainerView createContainerSwitch] | label: <UILabel: 0x7af7c6d0; frame = (0 0; 0 0); text = 'ON'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7af7c7b0>>
DEBUG | -[SwitchContainerView createContainerSwitch] | switch: <UISwitch: 0x7af7b370; frame = (0 0; 51 31); layer = <CALayer: 0x7af7b430>>
Any suggestions on determining which of the two is wider?

There is no need for you to "determine which is wider" - just set up the constraints and walk away, and let auto layout do its job. Simply use two constraints with inequalities / priorities to determine the width of the containing superview relative to the two subviews.
I achieved this arrangement using constraints alone - all I'm doing at this point is changing the text of the label:
Whenever the label text is short, the superview is 20 points wider than the switch. Whenever the label text is long, the superview is 20 points wider than the label. That is configured entirely with constraints. Here are the constraints that determine the width of the yellow superview in my screen shots (as shown in the debugger console):
UIView:0x7fdb03435450.width >= UISwitch:0x7fdb034355c0.width + 20 priority:999
UIView:0x7fdb03435450.width >= UILabel:0x7fdb03433260'Infundibulum'.width + 20 priority:999
The constraints do not change; I set them up once and the layout just works, and keeps working as the text of the label changes.

Related

UIScrollView with Auto Layout Constraints: Auto Content Size Calculation

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!

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

Center a variable number of UILabels vertically in a Cell/View

Is there a way to center a variable number of labels vertically within a superview (in my case a UITableViewCell)?
In this particular view in my app, I'd like to display data from my database. The data comes in a set that can range from 0 to 3 elements in size, so the view will have from 0 to 3 corresponding labels. If there is one label, it should appear on the vertical center line of the cell. If there are two labels, they should each appear equal distances above and below the vertical center line of the cell. If there are three labels, one should appear on the vertical center line and the other two should appear equal distances above and below the center line.
Hopefully this poor attempt at a visual helps. In the second example in this visual the two tick marks representing the labels appear as though they're the same distance apart as the top and bottom labels in the third example, but I'd prefer that in the real thing, they be slightly closer together than that, but such are the limits of plain text when trying to model complex UI elements.
**********************************************************************************
- -
-
- These two should be slightly closer to center than shown here.
-
-
- -
-
**********************************************************************************
I've tried fiddling with constraints as much as I could but can't seem to find a way to handle this in that way.
As I am from a web programming background, I'd describe this as an unordered list of variable height centered vertically in its parent container, but I'm not sure how to recreate that.
Assuming that containerView is a UIView within your cell serving a container for labels, the code below will do what you want.
NSLayoutConstraint* containerCenterXConstraint =
[NSLayoutConstraint constraintWithItem:containerView
attribute:NSLayoutAttributeCenterX
relatedBy:NSLayoutRelationEqual
toItem:containerView.superview
attribute:NSLayoutAttributeCenterX
multiplier:1
constant:0];
NSLayoutConstraint* containerCenterYConstraint =
[NSLayoutConstraint constraintWithItem:containerView
attribute:NSLayoutAttributeCenterY
relatedBy:NSLayoutRelationEqual
toItem:containerView.superview
attribute:NSLayoutAttributeCenterY
multiplier:1
constant:0];
[containerView.superview addConstraints:#[containerCenterXConstraint, containerCenterYConstraint]];
[containerView setTranslatesAutoresizingMaskIntoConstraints:NO];
UILabel* previousLabel = nil;
for (int i = 0; i < numberOfElements; i++) {
UILabel* label = [[UILabel alloc] init];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
label.text = <init label with data>;
[label sizeToFit];
[containerView addSubview:label];
NSArray* constraints;
NSDictionary* viewsDict;
NSDictionary* metricsDict = #{#"labelHeight": #(CGRectGetHeight(label.frame))};
if (i == 0 && numberOfElements == 1) {
viewsDict = NSDictionaryOfVariableBindings(label);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label(labelHeight)]|"
options:0
metrics:metricsDict
views:viewsDict];
} else if (i == 0 && numberOfElements > 1) {
viewsDict = NSDictionaryOfVariableBindings(label);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[label(labelHeight)]"
options:0
metrics:metricsDict
views:viewsDict];
} else if (i == (numberOfElements - 1) && numberOfElements > 1) {
viewsDict = NSDictionaryOfVariableBindings(label);
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"V:[label(labelHeight)]|"
options:0
metrics:metricsDict
views:viewsDict];
}
if (i > 0 && numberOfElements > 1) {
viewsDict = NSDictionaryOfVariableBindings(previousLabel, label);
constraints = [[NSLayoutConstraint constraintsWithVisualFormat:#"V:[previousLabel][label(labelHeight)]"
options:0
metrics:metricsDict
views:viewsDict] arrayByAddingObjectsFromArray:constraints];
}
[containerView addConstraints:constraints];
constraints = [NSLayoutConstraint constraintsWithVisualFormat:#"|[label]|"
options:0
metrics:nil
views:viewsDict];
[containerView addConstraints:constraints];
previousLabel = label;
}

Vertical list of UILabels and NSLayoutConstraint

I have a vertical list of UILabels:
I want to be able to have all the labels line up with the ":" on the right side and keep the spacing to the left side of the superView (createDate label stay put, and the name and year labels would shift to the right).
Code:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[nameLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(nameLabel)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:[headerView]-[nameLabel]-[createDateLabel]-[yearLabel]" options:NSLayoutFormatAlignAllLeading metrics:nil views:NSDictionaryOfVariableBindings(headerView, nameLabel, createDateLabel, yearLabel)]];
EDIT:
Ok, after implementing some suggestions:
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-(52)-[nameLabel]-[createDateLabel]-[yearLabel]" options:NSLayoutFormatAlignAllTrailing metrics:nil views:NSDictionaryOfVariableBindings(headerView, nameLabel, createDateLabel, yearLabel)]];
gives gets them all right aligned:
I would prefer to keep it pinned to the headerView so if that view changes height, I won't need to recode the pin space. Also, if I pin to headerView, it causes the labels to shift all the way to the right:
So that might just be a losing battle.
I still need to figure out how to pin them to the left and keep the ":" lined up. Right now, I pin createDateLabel because when I'm visually looking at it, I can see its the widest. Is there way I can get it to know which label will be the widest?
You can do this by :
Align trailing edges not leading
Pin leading space from superview to be >= [some const value]. This will make the labels have at least the given spacing from the left edge.
Pin the vertical spacing as you are
If you know which label will be the longest, you can also just pin that elements leading space to superview to your constant.
All of these constraints can be made in interface builder too, so that makes your life slightly easier
How about adding individual constraints to shorter labels:
- (void)viewDidLoad
{
[super viewDidLoad];
UILabel *createDateLabel = [[UILabel alloc] init];
createDateLabel.text = #"Created date:";
createDateLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:createDateLabel];
UILabel *yearLabel = [[UILabel alloc] init];
yearLabel.text = #"Year:";
yearLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:yearLabel];
UILabel *nameLabel = [[UILabel alloc] init];
nameLabel.text = #"Name:";
nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:nameLabel];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[createDateLabel]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(createDateLabel)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-40-[nameLabel]-[createDateLabel]-[yearLabel]" options:NSLayoutFormatAlignAllTrailing metrics:nil views:NSDictionaryOfVariableBindings(nameLabel, createDateLabel, yearLabel)]];
// skip these
// [self.view addConstraint:[NSLayoutConstraint constraintWithItem:nameLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:createDateLabel attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];
// [self.view addConstraint:[NSLayoutConstraint constraintWithItem:yearLabel attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual toItem:createDateLabel attribute:NSLayoutAttributeRight multiplier:1.0 constant:0.0]];
}
and the output is this:
I agree it is easier to achieve it in IB, but if you are forced to do it in code..
Good reading is this: Creating Individual Layout Constraints
Make all the labels the same width (set a definite width constraint on all of them) and make their text alignment all be right-aligned.
Here's my rendering. No code - this was set up entirely using constraints in Interface Builder. The longest one ("Create Date:") is adopting its natural width; the others have the same width. All contain right-aligned text, obviously. Other needed constraints are obvious.

Resizing UITextView Without Losing Constraints

I'm trying to figure out how to resize UITextView (and everything actually) without losing constraints. Basically, I'm trying to layout a page where most components can have variable sizes (like description). I tried doing it with a simple use case where I have a UITextView and a UIButton underneath. I want to make sure that the position of the button is relative to the bottom of the UITextView.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
CGRect frame = self.textView.frame;
int height = self.textView.contentSize.height;
frame.size.height = height;
self.textView.frame = frame;
}
What I ended up with is UITextView overlapping with UIButton. After doing a bit of research, it seems that if I replace the frame, all constraints are gone also. I tried copying the constraints over, but of course the pointer is still pointing at the old frame so that didn't help at all.
Is there a good way to solve a very dynamically laid out page? I'm trying to at least use interface builder rather than code everything.
EDITED
I tried updating the constraint as suggested, but that didn't actually resize the UITextView. Did I do it incorrectly? When I get the constant again, it's updated, but the height isn't changed visually. I did simplify my code by adding an IBOutlet for the constraint. Still no luck however.
int height = self.textView.contentSize.height;
self.textViewHeightConstraint.constant = height;
EDITED 2
I figured it out now. I had an extra constraint for the bottom and that was stopping me from actually resizing the UITextView.
The issue is how you've defined your button's top constraint. If it's to the label, when you adjust the label's height constraint, the button will move. For example, if doing it programmatically:
UILabel *label = [[UILabel alloc] init];
label.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:label];
label.text = #"Hello world";
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Submit" forState:UIControlStateNormal];
button.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:button];
NSDictionary *views = NSDictionaryOfVariableBindings(label, button);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[label]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|-[button]" options:0 metrics:nil views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|-[label]-[button]" options:0 metrics:nil views:views]];
NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:20];
[label addConstraint:heightConstraint];
Then, if you change the label's height constraint, the button will move:
heightConstraint.constant = 100;
[UIView animateWithDuration:1.0 animations:^{
[self.view layoutIfNeeded];
}];
If you've defined your UI in Interface Builder, select the button and check the top constraint of the button and make sure it's to the label, not the superview:
But, again, if the button's top constraint is to the label, when the label's height constraint changes, the button will move.

Resources