14

Is there any simple way which can help me to change position of dependent views dynamically using their content size?

I want to show several views in column which all have varying content. And I want them to be placed one after another (I've created layout using constraints which looks like this)

initial layout

But whenever I change content of labels and call sizeToFit, system seems to ignore layout.

after size to fit call

At the moment I'm interested only in height property, I know that constraining rect can be used too and in the past I wrote many categories on UIView to change sizes dynamically (I guess everyone did). But maybe there is a simple way which I don't know?

Nik
  • 7,782
  • 6
  • 54
  • 79

3 Answers3

44

-sizeToFit should not be called if you are using auto-layout. That's part of the 'old' system.

It looks like IB has inserted explicit heights into your constraints (the vertical bars next to the labels indicate this). Try selecting the labels and hitting Cmd+= to clear these.

For multiline labels you will also need to do the following in your view controller to make everything work correctly when rotating/resizing the view:

- (void)updateLabelPreferredMaxLayoutWidthToCurrentWidth:(UILabel *)label
{
    label.preferredMaxLayoutWidth =
        [label alignmentRectForFrame:label.frame].size.width;
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    [self updateLabelPreferredMaxLayoutWidthToCurrentWidth:self.label1];
    [self updateLabelPreferredMaxLayoutWidthToCurrentWidth:self.label2];
    [self updateLabelPreferredMaxLayoutWidthToCurrentWidth:self.label3];

    [self.view layoutSubviews];
}

Multiline labels expose one of the weaknesses of auto-layout. We have to update preferredMaxLayoutWidth to force the label to reflow and adjust its height, otherwise if the view is resized/rotated, auto-layout does not realize the label needs to be reflowed and resized.

Mike Weller
  • 44,483
  • 14
  • 126
  • 148
  • Thank you! Worked for me (I completely deleted and created new view, xcode seems to store some layout-related data implicitly) – Nik Apr 10 '13 at 15:28
  • Yeah, IB + autolayout has lots of problems right now. Hopefully Apple can improve it in the future. – Mike Weller Apr 10 '13 at 17:19
  • 1
    Could you explain your code more, particularly the use of `alignmentRectForFrame`. – Jason McCreary Oct 22 '13 at 15:42
  • I have the same solution achieved by subclassing UILabel: see my post below – catamphetamine Jan 11 '14 at 13:12
  • This works wonders, thanks for explaining concisely why it's better than `sizeToFit()`. I was able to do this without `layoutSubviews()` – Dean Aug 07 '14 at 11:21
  • Curious, I've tried putting this in place - although the method is called, it's not actually refreshing the hight of my UILabel. The width seems to get updated, but the label is never redrawn. Thoughts? – brandonscript Sep 17 '14 at 18:52
2

If you still want to use Auto Layout Constraint for your Label. This is a solution:

[self.lblBadgeValue sizeToFit];
self.constraintWidthBadgeLabel.constant =  self.lblBadgeValue.frame.size.width;
[self.lblBadgeValue needsUpdateConstraints];
[self.lblBadgeValue layoutIfNeeded];
  • Explain more:
  • sizeToFit -> make label fit height, width with content of it.
  • So in runtime you need to update constraint height, or width for label
  • After that you need to say for compiler know that what need update constraint.
  • And in the end you need to call layout if have change on constraint.
Linh Nguyen
  • 1,845
  • 1
  • 18
  • 15
-1

This works too. https://github.com/jszumski/auto-layout-table-cells/blob/master/DynamicCellHeights/JSLabel.m

@interface JSLabel : UILabel

@end

@implementation JSLabel

- (id)init {
    self = [super init];

    // required to prevent Auto Layout from compressing the label (by 1 point usually) for certain constraint solutions
    [self setContentCompressionResistancePriority:UILayoutPriorityRequired
                                          forAxis:UILayoutConstraintAxisVertical];

    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];

    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);

    [super layoutSubviews];
}

@end
catamphetamine
  • 3,245
  • 25
  • 24