0

Just playing around with Auto Layout and UIScrollView. What am I missing here? This displays a blue scrollView at the bottom of the screen but I cannot see the contentView. The view debugger has nothing to say for it either. Any ideas?

//
//  ViewController.m
//  Fit
//
//  Created by Adam Dahan on 2015-03-13.
//  Copyright (c) 2015 Adam Dahan. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()
{
    UIScrollView *scrollView;
    UIView *contentView;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    [self createViews];
    [self constrainScrollView];
    [self constrainContentView];
}

- (void)createViews
{
#pragma Initialize a scrollView
    scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
    scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.backgroundColor = [UIColor blueColor];
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    [self.view addSubview:scrollView];

#pragma Initialize a contentView for the scrollView
    contentView = [[UIView alloc] initWithFrame:CGRectZero];
    contentView.backgroundColor = [UIColor redColor];
    contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [scrollView addSubview:contentView];
}

- (void)constrainScrollView
{

#pragma scrollView vertical constraints
    NSArray *cns = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[scrollView(40)]|"
                                                       options:0
                                                       metrics:nil
                                                                views:NSDictionaryOfVariableBindings(scrollView)];
    [self.view addConstraints:cns];

#pragma scrollView horizontal constraints
    cns = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
                                              options:0
                                              metrics:nil
                                                views:NSDictionaryOfVariableBindings(scrollView)];
    [self.view addConstraints:cns];
}

- (void)constrainContentView
{

#pragma contentView vertical constraints
    NSArray *cns = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|"
                                              options:0
                                              metrics:nil
                                                views:NSDictionaryOfVariableBindings(scrollView, contentView)];
    [scrollView addConstraints:cns];

#pragma contentView horizontal constraints
    cns = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|"
                                              options:0
                                              metrics:nil
                                                views:NSDictionaryOfVariableBindings(scrollView, contentView)];
    [scrollView addConstraints:cns];
}

@end
arcognito
  • 48
  • 4
  • i think you need to set the content size: `scrollView.contentSize = `. You typically use the constraints on the scrollView but not on its content view. You set the scrollView's content size, and it handles the rest. – Josh Gafni Mar 14 '15 at 03:16

2 Answers2

1

You really should check out Technical Note TN2154 UIScrollView And Autolayout.

Your way let the system cannot decide scrollView's contentSize.

The main idea is let the scrollView knows what's the contentSize no matter which way you choose.

Puttin
  • 1,596
  • 23
  • 27
  • Then why does it require a contentSize in order to work? – arcognito Mar 17 '15 at 14:35
  • 1
    @arcognito actually the system will calculate the contentSize for you if you are using Auto Layout with ScrollView. So it need the constraints that can calculate the right size. – Puttin Mar 18 '15 at 09:00
1

ScrollViews are a little bit more complicated when using auto layout. The frame is defined from the constraints with the other views (for you would be the ones you add in constainScrollView method).

But the contentSize is defined depending on the constraints from the views inside the scrollView. For your case, the contentSize is (0,0) because the inner view has no width or height constraint.

I suppose you would like this inner view to fill the scrollview. Then your constraints should look something like this:

- (void)constrainContentView
{
    [self.view layoutIfNeeded]; // This causes the scrollview width and height to be calculated based on the constraints you sent in the previous method
    // You could check by adding a breakpoint here, if the scrollview has the frame you would expect

#pragma contentView vertical constraints
    NSArray *cns = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[contentView(height)]-|"
                                              options:0
                                              metrics:@{"height":@(CGRectGetHeight(scrollView.frame))}
                                                views:NSDictionaryOfVariableBindings(scrollView, contentView)];
    [scrollView addConstraints:cns];

#pragma contentView horizontal constraints
    cns = [NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[contentView(width)]-|"
                                              options:0
                                              metrics:@{"width":@(CGRectGetWidth(self.view.frame))}
                                                views:NSDictionaryOfVariableBindings(scrollView, contentView)];
    [scrollView addConstraints:cns];
}

The actual value you set for the width and height depends on your needs.

The important thing is that the views inside the scrollView need to have the constraints in such a way setup that the contentSize can be calculated without ambiguity. This means if the view does not have an intrinsic size (like a UIButton or UILabel) you need to add the width/height constraints explicitly.

Because of setting the width and height explicitly, on orientation change you would need to update the constraints so that the views inside have the right dimensions.

Hope this fixes your problem.

Catalina T.
  • 3,456
  • 19
  • 29
  • Thank you for your answer :) I think I was confused by the VFL because from my understanding H:|-[view]-| (pipe-view-pipe) means superview width. So I would have thought that would naturally expand the entire width of the screen. – arcognito Mar 25 '15 at 00:04
  • You're welcome :) Yeah, that is why auto layout is a bit tricky when it comes to `scrollViews`, because they make a difference between the constraints that are used to define their `frame` and the ones used to define the `contentSize`. One would not expect that they are different. Anyway, great that you fixed your problem – Catalina T. Mar 26 '15 at 08:11