How do I auto size a UIScrollView to fit its content

Is there a way to make a UIScrollView auto-adjust to the height (or width) of the content it's scrolling?

Something like:

[scrollView setContentSize:(CGSizeMake(320, content.height))];

Solution 1:

The best method I've ever come across to update the content size of a UIScrollView based on its contained subviews:

Objective-C

CGRect contentRect = CGRectZero;

for (UIView *view in self.scrollView.subviews) {
    contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;

Swift

let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
    rect = rect.union(view.frame)
}
scrollView.contentSize = contentRect.size

Solution 2:

UIScrollView doesn't know the height of its content automatically. You must calculate the height and width for yourself

Do it with something like

CGFloat scrollViewHeight = 0.0f;
for (UIView* view in scrollView.subviews)
{
   scrollViewHeight += view.frame.size.height;
}

[scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))];

But this only work if the views are one below the other. If you have a view next to each other you only have to add the height of one if you don't want to set the content of the scroller larger than it really is.

Solution 3:

Solution if you're using auto layout:

  • Set translatesAutoresizingMaskIntoConstraints to NO on all views involved.

  • Position and size your scroll view with constraints external to the scroll view.

  • Use constraints to lay out the subviews within the scroll view, being sure that the constraints tie to all four edges of the scroll view and do not rely on the scroll view to get their size.

Source: https://developer.apple.com/library/ios/technotes/tn2154/_index.html

Solution 4:

I added this to Espuz and JCC's answer. It uses the y position of the subviews and doesn't include the scroll bars. Edit Uses the bottom of the lowest sub view that is visible.

+ (CGFloat) bottomOfLowestContent:(UIView*) view
{
    CGFloat lowestPoint = 0.0;

    BOOL restoreHorizontal = NO;
    BOOL restoreVertical = NO;

    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if ([(UIScrollView*)view showsHorizontalScrollIndicator])
        {
            restoreHorizontal = YES;
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:NO];
        }
        if ([(UIScrollView*)view showsVerticalScrollIndicator])
        {
            restoreVertical = YES;
            [(UIScrollView*)view setShowsVerticalScrollIndicator:NO];
        }
    }
    for (UIView *subView in view.subviews)
    {
        if (!subView.hidden)
        {
            CGFloat maxY = CGRectGetMaxY(subView.frame);
            if (maxY > lowestPoint)
            {
                lowestPoint = maxY;
            }
        }
    }
    if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
    {
        if (restoreHorizontal)
        {
            [(UIScrollView*)view setShowsHorizontalScrollIndicator:YES];
        }
        if (restoreVertical)
        {
            [(UIScrollView*)view setShowsVerticalScrollIndicator:YES];
        }
    }

    return lowestPoint;
}