resize superview after subviews change dynamically using autolayout

78,609

Solution 1

If you're using Auto Layout, here's what you need to do:

  1. Make sure you aren't adding fixed width and/or height constraints to any of your subviews (depending on which dimension(s) you want to dynamically size). The idea is to let the intrinsic content size of each subview determine the subview's height. UILabels come with 4 automatic implicit constraints which will (with less than Required priority) attempt to keep the label's frame at the exact size required to fit all the text inside.

  2. Make sure that the edges of each label are connected rigidly (with Required priority constraints) to the edges of each other and their superview. You want to make sure that if you imagine one of the labels growing in size, this would force the other labels to make room for it and most importantly force the superview to expand as well.

  3. Only add constraints to the superview to set its position, not size (at least, not for the dimension(s) you want it to size dynamically). Remember that if you set the internal constraints up correctly, its size will be determined by the sizes of all the subviews, since its edges are connected to theirs in some fashion.

That's it. You don't need to call sizeToFit or systemLayoutSizeFittingSize: to get this to work, just load your views and set the text and that should be it. The system layout engine will do the calculations for you to solve your constraints. (If anything, you might need to call setNeedsLayout on the superview...but this shouldn't be required.)

Solution 2

Use container views

In the following example I have a 30x30 image, and the UILabel is smaller than the containing view with the placeholder text. I needed the containing view to be at least as big as the image, but it needed to grow to contain multi-line text.

In visual format the inner container looks like this:

H:|-(15.0)-[image(30.0)]-(15.0)-[label]-(15.0)-|
V:|[image(30.0)]|
V:|[label(>=30.0)]|

Then, set the containing view to match the height of the label. Now the containing view will ride the size of the label.

As @smileyborg pointed out in his answer, connecting the content rigidly to the superview informs the layout engine that the simple container view should cause it to grow.

Yellow alignment rectangles

If you want the yellow alignment rectangles add -UIViewShowAlignmentRects YES in your scheme's list of run arguments.

UILabel that is multi-line

Solution 3

This almost follows @smileyborg answer and comes with a concrete example.

enter image description here

Won't describe all constraints, but those related to the calculation of the height of UI objects.

  1. [Label] Labels must not have a fixed height constraint, in this case, AutoLayout won't resize labels to fit the text, so setting edge constraints is the key. (green arrows)
  2. [Subview] Steps 1 and 3 are very easy to follow, but this step can be misunderstood. As in the case with labels, subviews must not have height constraint set. All subviews must have top constraint set, ignoring bottom constraint, which can make you think will trigger unsatisfied constraint exception at runtime, but it won't if you set bottom constraint for the last subview. Missing to do so will blow the layout. (red arrows)

  3. [Superview] Set all constraints the way you need, but pay big attention to the height constraint. Assign it a random value, but make it optional, AutoLayout will set the height exactly to fit the subviews. (blue arrows)

This works perfectly, there is no need to call any additional system-layout update methods.

Solution 4

This was made dramatically easier with the introduction of Stack Views in iOS 9. Use a stack view inside your view to contain all your content that resizes, and then simply call

view.setNeedsUpdateConstraints()
view.updateConstraintsIfNeeded()
view.setNeedsLayout()
view.layoutIfNeeded()

after changing your content. Then you can get your new size by calling

view.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)

if you ever need to calculate the exact size required for a view.

Share:
78,609

Related videos on Youtube

Pedroinpeace
Author by

Pedroinpeace

Updated on July 08, 2022

Comments

  • Pedroinpeace
    Pedroinpeace almost 2 years

    I cant for the love of god the the hang of this resizing superview.

    I have a UIView *superview with 4 UILabels. 2 function as header for the 2 others.

    The content in all 4 are dynamic coming from database.

    SizeToFit vs SizeThatFits:(CGSize) vs UIView systemLayoutSizeFittingSize:, passing either UILayoutFittingCompressedSize or UILayoutFittingExpandedSize.

    I use autolayout programatically and have set the superview height to be equal or greater to a dummy number.

    where and how do I use these SizeToFit vs sizeThatFits:(CGSize) vs UIView systemLayoutSizeFittingSize:, passing either UILayoutFittingCompressedSize or UILayoutFittingExpandedSize. I have read a lot of tips here on stack but ended up with nothing.

    DO I need to recalculate the constraints for the superview somewhere specific. Maby setting the height to be ´@property` in its controller class and remove and readd it? Atm I have tried to put everything everywhere and then some. Still I get the same size end result with the dummy height and text floating outside. Even after setting clipsToBound on subview.

    I am scratching my hair of.. help

    • esh
      esh about 10 years
      Hey, could you post your code somewhere on pastebin or something if I could take a look? I am suffering from a similar problem. It has been a week or more.
  • Pedroinpeace
    Pedroinpeace over 10 years
    Thanks smileyborg. A late respons since I was in middle of a project and didnt quite get what you ment. Now I have had the chance to work some more with constraints in programmatically and especially with constraintsWithVisualFormatand I totally get your point about the containing subviews stretching and kinda pushing out the superview to its size :) Thanks again
  • Ralfonso
    Ralfonso over 8 years
    -UIViewShowAlignmentRects YES -- great tip!
  • da Rocha Pires
    da Rocha Pires over 8 years
    In this case, the container only has to adapt it's height to the label. What if the container should adapt to the sum of the heights of more than one view?
  • Omer
    Omer over 8 years
    I agree with the solution, but it's not working for me, check stackoverflow.com/questions/34635838/…
  • Chris
    Chris about 8 years
    This answer is magic. So much less effort on my part making the views look good.
  • KudoCC
    KudoCC over 7 years
    It should be mentioned that you should set translatesAutoresizingMaskIntoConstraints = NO if you're using xib.
  • karim
    karim almost 7 years
    translatesAutoresizingMaskIntoConstraints = NO is the answer for my case. But now the container view lost frame and its origin is always (0,0), I cant put it to the place I wish. stackoverflow.com/questions/45548372/…
  • Mansuu....
    Mansuu.... over 6 years
    I am using StackView and adding views dynamically, these views are not getting resized.@JacobRuth
  • Michele Dall'Agata
    Michele Dall'Agata over 5 years
    @smileyborg That doesn't seem to work when I design an UIViewController stored in a separate storyboard. I had to add the size of my collection view inside the superview (the controller view) because otherwise the collection view was empty at runtime.
  • Winston Du
    Winston Du almost 4 years
    @smileyborg, this is useful advice, but what if I do want to have a height constraint on my subviews?
  • Faisal Naseer
    Faisal Naseer over 3 years
    how to make a height optional.. I am new to iOS so cant figure it out.thanks