Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS Autolayout with UIScrollview: Why does content view of scroll view not fill the scroll view?

The following code (called in viewDidLoad) results in a fully red screen. I would expect it to be a fully green screen. Why is it red? And how can I make it all green?

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView,contentView);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
like image 964
dragosaur Avatar asked May 30 '13 18:05

dragosaur


People also ask

Why is scroll view not scrolling?

To fix ScrollView Not scrolling with React Native, we wrap the content of the ScrollView with the ScrollView. to wrap ScrollView around the Text components that we render inside. As a result, we should see text inside and we can scroll up and down.

Can stack view be scrollable?

It's not necessary to combine manual and auto layout to allow for a scrollable stack view. You simply need to constrain the stack view's width and/or height.

What is content layout guide in ScrollView?

Content Layout Guide Constraints The constraints to the content layout guide are special constraints that have no effect on the size or position of your content view. These constraints tell the UIScrollView the boundaries of your content (sets the contentSize of the UIScrollView ).


2 Answers

Constraints with scroll views work slightly differently than it does with other views. The constraints between of contentView and its superview (the scrollView) are to the scrollView's contentSize, not to its frame. This might seem confusing, but it is actually quite useful, meaning that you never have to adjust the contentSize, but rather the contentSize will automatically adjust to fit your content. This behavior is described in Technical Note TN2154.

If you want to define the contentView size to the screen or something like that, you'd have to add a constraint between the contentView and the main view, for example. That's, admittedly, antithetical to putting content into the scrollview, so I probably wouldn't advise that, but it can be done.


To illustrate this concept, that the size of contentView will be driven by its content, not by the bounds of the scrollView, add a label to your contentView:

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

UILabel *randomLabel = [[UILabel alloc] init];
randomLabel.text = @"this is a test";
randomLabel.translatesAutoresizingMaskIntoConstraints = NO;
randomLabel.backgroundColor = [UIColor clearColor];
[contentView addSubview:randomLabel];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];

[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];

Now you'll see that the contentView (and, therefore, the contentSize of the scrollView) are adjusted to fit the label with standard margins. And because I didn't specify the width/height of the label, that will adjust based upon the text you put into that label.


If you want the contentView to also adjust to the width of the main view, you could do redefine your viewDict like so, and then add these additional constraints (in addition to all the others, above):

UIView *mainView = self.view;

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel, mainView);

[mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[contentView(==mainView)]" options:0 metrics:0 views:viewDict]];

There is a known issue (bug?) with multiline labels in scrollviews, that if you want it to resize according to the amount of text, you have to do some sleight of hand, such as:

dispatch_async(dispatch_get_main_queue(), ^{
    randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
like image 106
Rob Avatar answered Oct 18 '22 20:10

Rob


I tried Rob's answer looks fine at first. BUT! if you also enabled zooming, this Autolayout code will get in the way. It keeps resizing the contentView when zooms. That is, if I zoomed in (zoomScale > 1), I won't be able to scroll the the parts outside the screen.

After days of fighting with Autolayout, sadly I couldn't find any solution. In the end, it doesn't even matter (wat? ok, sorry). In the end, I just remove Autolayout on contentView (use a fixed size contentView), and then in layoutSubviews, I adjust the self.scrollView.contentInset. (I'm using Xcode5, iOS 7)

I know this is not a direct solution to this question. But I just want to point out an easy workaround. It works perfectly for centering a fixed-size contentView in a scrollView, with just 2 lines of code! Hope it may help someone ;)

- (void)layoutSubviews {
    [super layoutSubviews];

    // move contentView to center of scrollView by changing contentInset,
    // because I CANNOT set this with AUTOLAYOUT!!!
    CGFloat topInset = (self.frame.size.height - self.contentView.frame.size.height)/2;
    self.scrollView.contentInset = UIEdgeInsetsMake(topInset, 0, 0, 0);
}
like image 1
Hlung Avatar answered Oct 18 '22 20:10

Hlung