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, 2014 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, 2014 at 23:10
  • \$\begingroup\$ I believe so. Tomorrow I will take a look \$\endgroup\$
    – nhgrif
    Oct 31, 2014 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, 2014 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, 2014 at 23:21

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.