3
\$\begingroup\$

I wanted a utility function on UIScrollView to automatically size the contentSize based on the content inside the scrollView. How is this?

#import "UIScrollView+UIScrollViewUtilities.h"
#import <objc/runtime.h>

static const int BOTTOM_CONTENT_OFFSET = 75;

@implementation UIScrollView (UIScrollViewUtilities)

- (void)setRecursiveSubviews:(NSMutableArray *)recursiveSubviews {
    objc_setAssociatedObject(self, @selector(recursiveSubviews), recursiveSubviews, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSMutableArray *)recursiveSubviews {
    NSMutableArray *array = objc_getAssociatedObject(self, @selector(recursiveSubviews));
    if (array == nil) {
        array = [[NSMutableArray alloc] init];
        [self setRecursiveSubviews:array];
    }
    return array;
}

- (void)autoAdjustContentSize {
    [self autoAdjustContentSize:self];
}

/*
 * This method is used to dynamically set the contentSize
 * of a UIScrollView based on its content.
 */
- (void)autoAdjustContentSize:(UIView *)view {

    // For all subviews of view passed in, add them to our
    // recursiveSubviews array and call this method recursively
    // to ensure we add all subviews (even grandchildren subviews)
    for (UIView *subView in [view subviews]) {
        NSMutableArray *array = [self recursiveSubviews];
        [array addObject:subView];
        [self setRecursiveSubviews:array];

        [self autoAdjustContentSize:subView];
    }

    // Of all of our subviews found, figure out which one
    // is the bottom-most element, and remember its y val
    CGFloat currentBottomValue = 0;
    for (UIView *subView in [self recursiveSubviews]) {
        CGFloat thisElementsBottomValue = subView.frame.origin.y + subView.frame.size.height;

        // Ignore UIViews because they are just blank space by themselves
        if (![subView isMemberOfClass:[UIView class]] && thisElementsBottomValue > currentBottomValue) {
            currentBottomValue = thisElementsBottomValue;
        }
    }

    // Finally, update the contentSize of the scrollView based on the bottom most element found on screen
    [self setContentSize:CGSizeMake([[UIScreen mainScreen] bounds].size.width, currentBottomValue + BOTTOM_CONTENT_OFFSET)];
}

@end
\$\endgroup\$
6
  • \$\begingroup\$ Why don't you just use auto-layout? \$\endgroup\$ – nhgrif Oct 31 '14 at 22:41
  • \$\begingroup\$ @nhgrif I do use auto-layout. There is a way to auto size the contentSize property of a UIScrollView with auto-layout? I'm not aware of that. \$\endgroup\$ – Adam Johns Oct 31 '14 at 23:10
  • \$\begingroup\$ I believe so. Tomorrow I will take a look \$\endgroup\$ – nhgrif Oct 31 '14 at 23:15
  • \$\begingroup\$ I think the trick is to add all of your stuff to a new UIView (which you can size with auto-resizing constraints), then add that UIView to the scrollview. So the scroll view's content view has exactly one subview, and then it should work correctly. \$\endgroup\$ – nhgrif Nov 1 '14 at 14:54
  • \$\begingroup\$ @nhgrif I think you are right. I think I can set the UIView's height to be based on the bottom most element in storyboard which should accomplish the same functionality using auto-layout with no code. I'll try it out later. \$\endgroup\$ – Adam Johns Nov 1 '14 at 23:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.