58
votes
-ViewController
--View
---ScrollView (Top,Bottom,Leading,Trailing spaces to superview set to 0)
----ContentView (Top,Bottom,Leading,Trailing spaces to superview set to 0, Width equals with View (ViewController's child))
-----Label1
-----etc...

If i set a specific height constraint for my content view (e.g. 1000) the ScrollView works fine, but i got a static ContentView of 1000, which is not my goal, because my contentView got dynamic content, so it should be the height the content needs.

If i delete the height constraint for my ContentView, Xcode says:

Missing Constraints:
ScrollView need constraints for: Y position or height

ScrollView is actually pinned at top and bottom, so i don't know why Xcode want's a height constraint for the ScrollView...

What is the right way to go, when i want the ContentView height to be the height the content needs?

8

8 Answers

171
votes

Whenever using ScrollView with auto layout always follow below steps,

  1. ScrollView constraints: leadingSpace, topSpace, TrailingSpace, bottomSpace to superView and make sure when you control drag to add constraint, add it by pressing alt so that the constraint would be set without margin.

  2. Add UIView inside scroll view as container view and set its constraints: leadingSpace, topSpace, trailingSpace, bottomSpace to ScrollView without pressing alt button and set equalWidth to ScrollView.

  3. Whatever views you add inside this container view must have top to bottom constraint that is all view's should have vertical constraint, so containerView can calculate height required for itself based on the content inside it.

If the constraints are set correctly then the scrollView will set its content size automatically based on the component inside it and you do not need to set the content size manually, also the scrollView will only scroll if the component inside the container view is not fitting inside otherwise it won't scroll. If you want to make it scroll anyways then you need to check the Bounces Vertically property from storyboard to get the bounce effect.

Note: While you set constraint to the component inside the scrollView, you will see the constraint warning till you set the constraint from top component to the bottom one, aways remember that your top component should have top constraint (Vertical constraint) to superView and the component at the bottom should have bottom space constraint to the super view. When this satisfy then all warning will disappear eventually.

ScrollView constraints:

enter image description here

ContainerView constraints:

enter image description here

7
votes

Xcode 11

I've been following several tutorials on this for a few hours now, and none of them seem to work. It seems that Xcode 11 has some updates that change how the scroll views work with auto layout.

  1. Add a UIScrollView to the view and add top, bottom, leading, and trailing constraints.
  2. Add a UIView to the scroll view. We will call this the content view.
  3. Add top, bottom, leading, and trailing constraints from the content view to the scroll view's Content Layout Guide. Set the constraints to 0.
  4. Add an equal width constraint between the content view and the scroll view's Frame Layout Guide. (Not the scroll view or the main view!)
  5. Temporarily add a height constraint to the content view so that you can add your content. Make sure that all content has top, bottom, leading, and trailing constraints.
  6. Delete the height constraint on the content view.

    I found an excellent tutorial at this link.

3
votes
  1. Select your view controller
  2. Uncheck 'Adjust Scroll View Insets'

enter image description here

2
votes

Make sure the bottommost element inside of your content view inside of your scroll view has bottom space constraint set to 0 and make sure all elements inside content view have height set.

This makes content view resize according to elements inside it and you don't have fixed height of your scroll view/content view and your scroll view will resize with phone's screen size.

1
votes

It will work

-ViewController
--View
---ScrollView (Top,Bottom,Leading,Trailing spaces to superview set to 0)
----ContentView (Top,Leading,Trailing spaces to ScorllView set to 0 | Height to dynamic (1000))
-----Label1
-----etc...

Do same for your ContentView , Your Own Calculation for your Content

- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:YES];
[self adjustHeightOfTableview];
}

- (void)adjustHeightOfTableview
{
CGFloat height = tblMainMenu.contentSize.height;
CGFloat maxHeight = tblMainMenu.superview.frame.size.height - tblMainMenu.frame.origin.y;

// if the height of the content is greater than the maxHeight of
// total space on the screen, limit the height to the size of the
// superview.

if (height > maxHeight)
    height = maxHeight;

// now set the height constraint accordingly
self.tableViewHeightConstraint.constant = height;
[UIView animateWithDuration:0.1 animations:^{
    [self.view setNeedsUpdateConstraints];
}];
}

Edit 1

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
    NSString *OptName_length = @" Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam";
    NSDictionary *attributes = @{NSFontAttributeName: [UIFont systemFontOfSize:17.0]};
    CGRect rect = [OptName_length boundingRectWithSize:CGSizeMake(tableView.bounds.size.width-150.0, MAXFLOAT)
                                                  options:NSStringDrawingUsesLineFragmentOrigin
                                               attributes:attributes
                                                  context:nil];

    CGSize requiredSize = rect.size;
    return requiredSize.height +5.0f;
    }

Edit 2

Check the site, this will help you... uitableviewcell-dynamic-height-with-autolayout-conclusion

1
votes

Bharat Modi's answer pretty much solves the issue, but I (Xcode 8.3.2, iOS 10 SDK) also had to match the Content View's height and width to UIScrollView's height and width.

This will pin the content size to just visible area, but it makes IB happy. Then (I think) it's just a matter of calculating the content size correctly and setting it programmatically. Which I would've had to do anyway, since if you have dynamic content you can only do that at run time.

You might also need to tweak the "Adjust scroll view insets" settings of the view controller if you have a navigation bar.

This is what I have:

UIScrollView in IB without warnings

Note that this could be just a peculiarity of my setup, because the official suggestions (https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html) also don't mention that this needs to be done.

I tried figuring this out using just a scroll view, a content view and another dummy view of fixed width and height to imitate the content, but nothing seemed to make IB happy.

(Sorry, I don't have enough rep to put this as a comment on the original answer).


UPDATE:

So actually what I ended up doing is setting my Content View to a fixed height in IB and creating an outlet for that height constraint in the code. Then after the view has loaded all of its dynamic content I just update the height constraint like so:

self.uiContentViewHeightConstraint.constant = mapViewBottomY + 15

And everything works. The key here is basically that when there's a view inside a scroll view - that view defines scroll view's content size. As people have mentioned before, size of content view has to be fully defined at build time in order for IB to be happy. You are free to manipulate the values at run time though through outlets - it's easy and safe.

One advantage of having the content view at fixed size in IB is that since you've fixed the content size, views inside the content view can now be defined as usual, using relative offsets.

1
votes

If you have UIScrollView with UITextView inside complaining that:

"ScrollView needs constraint for y position or height". Or "UIScrollView Scrollable Content Size Ambiguity"

Just uncheck default “Scrolling Enabled” on UITextView in the IB. Works like magic :) enter image description here

-1
votes

Set your label left top right and bottom margin constraint with content view of scrollview, and set number of line of UILable to 0. It will expand your content view as per the size of your content

enter image description here