UIScrollView with iOS Auto Layout Constraints: Wrong size for subviews

I’m trying to generate a view in code. Here’s the hierachy of my view object

  • UIScrollView
    • UIView
      • UIButton

The ScrollView should be the same size as the window.
The button should be as big as possible.
I’m using iOS auto layout, so the constraint strings for all of my objects look like this

  • Use uicollectionview inside uitableviewcell
  • UIPrintInteractionController not displaying in iPad
  • Updating apps to iOS6
  • call popToRootViewController from another tab
  • How to wait for method that has completion block (all on main thread)?
  • Cannot convert value of type 'NSRange' (aka '_NSRange') to expected type 'Range<Index>'(aka 'Range<String.CharacterView.Index>')
  • H:|[object]|
    V:|[object]|
    

    I’ve also set translatesAutoresizingMaskIntoConstraints to NO for each object.

    The problem is that the button only gets the default button-size. Its parent view object (UIView) only gets the size its subviews need.

    enter image description here

    red: UIScrollView / yellow: UIView

    How can I force those views to be as big as the scrollView?

    When I use a UIView instead of th UIScrollView everything works great…

    Here’s some code:

        - (void) viewDidLoad {
    
            [super viewDidLoad];
    
            // SCROLL VIEW
            UIScrollView* scrollView = [UIScrollView new];
            scrollView.backgroundColor=[UIColor redColor];
            scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    
            //CONTAINER VIEW
            UIView *containerView = [UIView new];
            containerView.translatesAutoresizingMaskIntoConstraints = NO;
            containerView.backgroundColor = [UIColor yellowColor];
            [scrollView addSubview:containerView];
    
            // CONSTRAINTS SCROLL VIEW - CONTAINER VIEW
            [scrollView addConstraints:
             [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[containerView]|"
                                                     options:0 metrics:nil
                                                       views:@{@"containerView":containerView}]];
            [scrollView addConstraints:
             [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[containerView]|"
                                                     options:0 metrics:nil
                                                       views:@{@"containerView":containerView}]];
    
            // BUTTON
            UIButton* button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
            button.translatesAutoresizingMaskIntoConstraints = NO;
            [button setTitle:@"I'm way to small" forState:UIControlStateNormal];
            [containerView addSubview:button];
    
            // CONSTRAINTS CONTAINER VIEW - BUTTON
            [containerView addConstraints:
             [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[button]|"
                                                     options:0 metrics:nil
                                                       views:@{@"button":button}]];
            [containerView addConstraints:
             [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[button]|"
                                                     options:0 metrics:nil
                                                       views:@{@"button":button}]];
            self.view = scrollView;
    
        }
    

    UPDATE:
    I really don’t know, why this is happening. If you set up the view in IB, connect the outlets and instanciate the view in code, the scrollview behaves like a normal view (which bounces vertically). Its contentSize is not calculated correctly. More here. But how to do it correctly?

    Solutions Collect From Internet About “UIScrollView with iOS Auto Layout Constraints: Wrong size for subviews”

    A couple of observations:

    1. Constraints for subviews in scroll views don’t work like constraints in other views. They’re used to set the contentSize of the scroll view. (See TN2154.) That way, you throw a bunch of stuff on a scroll view, set the constraints for the stuff inside it, and the contentSize is calculated for you. It’s very cool feature, but it’s antithetical to what you’re trying to do here.

    2. Worse, buttons will, unless you set an explicit constraint for their width and height of a button, will resize according to their content.

    The net effect of these two observations is that your existing constraints say “(a) set my container to be the size of my button; (b) let my button resize itself dynamically to the size of the text; and (c) set my scrollview’s contentSize according to the size of my container (which is the size of the button).”

    I’m unclear as to what the business problem is. But here are some constraints that achieve what I think your technical question was:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        UIView *view = self.view;
    
        UIScrollView *scrollView = [[UIScrollView alloc] init];
        scrollView.backgroundColor = [UIColor redColor]; // just so I can see it
        scrollView.translatesAutoresizingMaskIntoConstraints = NO;
        [self.view addSubview:scrollView];
    
        UIView *containerView = [[UIView alloc] init];
        containerView.backgroundColor = [UIColor yellowColor]; // just so I can see it
        containerView.translatesAutoresizingMaskIntoConstraints = NO;
        [scrollView addSubview:containerView];
    
        UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        button.translatesAutoresizingMaskIntoConstraints = NO;
        [button setTitle:@"I'm the right size" forState:UIControlStateNormal];
        [containerView addSubview:button];
    
        NSDictionary *views = NSDictionaryOfVariableBindings(scrollView, button, view, containerView);
    
        // set the scrollview to be the size of the root view
    
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:views]];
    
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:views]];
    
        // set the container to the size of the main view, and simultaneously
        // set the scrollview's contentSize to match the size of the container
    
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[containerView(==view)]|"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:views]];
    
        [view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[containerView(==view)]|"
                                                                           options:0
                                                                           metrics:nil
                                                                             views:views]];
    
        // set the button size to be the size of the container view
    
        [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[button(==containerView)]"
                                                                              options:0
                                                                              metrics:nil
                                                                                views:views]];
    
        [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[button(==containerView)]"
                                                                              options:0
                                                                              metrics:nil
                                                                                views:views]];
    
    }
    

    Frankly, I don’t understand the business intent of your UI, as this feels like a contortion of auto layout to achieve a very simply UI. I don’t know why you have a scroll view if you have “screen sized” content in it (unless you were paging through buttons). I don’t know why you’d have a content view with a single item in it. I don’t understand why you’re using a full-screen button (I’d just put a tap gesture on the root view at that point and call it a day).

    I’ll assume you have good reasons for all of this, but it might make sense to back up, ask what is your desired user experience is, and then approach the problem fresh to see if there’s a more efficient way to achieve the desired effect.