Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


How to add constraints for views in the UIScrollView:

  • Add top, right, bottom, left spacing to the edges of the content view, in addition, also the width and height of these views.
  • Imaging the positions of views in the content view.

An easy way to deal with AutoLayout with scrollview is to add a container view containing all subviews in the scroll view.

The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.

The following figure will depicts that:

There are so many questions about using AutoLayout with UIScrollView, the key point which we ignore is that the inner views of the UIScrollView make constraints against the Content View but not the UIScrollView itself. Refer to the Technical Note TN2154, you can find:

UIScrollView will calculate the size of the content view according to the constraints of the inner views. (For the example, the content size: width = 100(leading space) + 200 (view's width) + 500 (trailing space), height = 131 (top spacing) + 200(height) + 269(bottom spacing)

You can find the trailing space is 500 points, if the constraint is made to the UIScrollView, the view will be miss placed and should be update its frame. However, no warnings and no errors. Because all the constraints are against the content view.

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


Add a Content View

Add a Scroll View

  • Green view: pin the top, left, and bottom edges. Make the width 400.
  • If we were making the content scroll vertically, then we would set the content view's width to be equal to the scroll view's parent's width.
  • Make sure that all of the scrollable content has a set width and is pinned on all sides.
  • Make the content view and the scroll view's parent have equal heights for horizontal scrolling. (Equal widths for vertical scrolling)
  • Purple view: pin all four edges edges. Make the width whatever the remaining space is (268 in this case).
  • Red view: pin the top, left, and bottom edges. Make the width 300.
  • The UIScrollView should only use one subview. This is a 'UIView' that serves as content view to hold everything you wish to scroll.

Add a UIScrollView and pin all four sides to the root view of the view controller.

Add a UIView as a subview to the scroll view. This is key. Don't try to add lots of subviews to the scroll view. Just add a single UIView. This will be your content view for the other views you want to scroll. Pin the content view to the scroll view on all four sides.

Add three UIViews and give them all constraints. I used 8 point margins for everything.

For vertical scrolling, just swap all the width and height directions in this example (tested and working).

In this example we will make a horizontal scroll view. Select the View Controller and then choose Freeform in the Size Inspector. Make the width 1,000 and the height 300. This just gives us room on the storyboard to add content that will scroll.

It can be just a single view application.

Judging by the high number of votes on the question and the low number of votes on the answers, people are not finding an understandable and quick solution here. Let me try to add one. This project is a self-contained example done completely in the Interface Builder. You should be able to work through it in 10 minutes or less. Then you can apply the concepts you learned to your own project.

Now in the Document Outline, Command click both the content view and the scroll view's parent view in order to select them both. Then set the heights to be equal (Control drag from the Content View to the Scroll View). This is also key. Because we are scrolling horizontally, the scroll view's content view won't know how high it should be unless we set it in this way.

That's all. You can run your project now. It should behave like the scrolling image at the top of this answer.

The original question asks about scrolling buttons. Here I just use UIViews but they can represent whatever view you like. I also chose horizontal scrolling because the storyboard screenshots are more compact for this format. The principles are the same for vertical scrolling, though.

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


I assume you are running into issues with thecontentSize. Check out this blog post on how to handle the contentSize when using a "pure" AutoLayout approach. The gist of it is that your constraints implicitly define the content size. You NEVER set it explicitly when using AutoLayout. I've attached example project at the end of the blog post to demonstrate how it works

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


UIButton *buttonA                   = [[UIButton alloc] init];
    UIButton *buttonB                   = [[UIButton alloc] init];
    UIButton *buttonC                   = [[UIButton alloc] init];
    [scrollView addSubview:buttonA];
    [scrollView addSubview:buttonB];
    [scrollView addSubview:buttonC];
    buttonA.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonB.translatesAutoresizingMaskIntoConstraints       = NO;
    buttonC.translatesAutoresizingMaskIntoConstraints       = NO;

    viewsDictionary                     = NSDictionaryOfVariableBindings(scrollView, buttonA, buttonB, buttonC);

    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[buttonA]-|"
                                                                       options:kNilOptions
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
    [scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[buttonA]-[buttonB]-[buttonC]-|"
                                                                       options:NSLayoutFormatAlignAllBaseline
                                                                       metrics:nil
                                                                         views:viewsDictionary]];
UIView *containerView               = [[UIView alloc] init];
    UIScrollView *scrollView            = [[UIScrollView alloc] init];
    [containerView addSubview:scrollView];
    containerView.translatesAutoresizingMaskIntoConstraints = NO;
    scrollView.translatesAutoresizingMaskIntoConstraints    = NO;
    NSDictionary *viewsDictionary       = NSDictionaryOfVariableBindings(containerView, scrollView);

    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil
                                                                            views:viewsDictionary]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
                                                                          options:kNilOptions
                                                                          metrics:nil

For example, is you have a UIScrollView inside of a UIView it will look like this (as I am sure you are aware):

I only realized after second reading that you are setting up a CONTAINERview and not a CONTENTview. Based on your code, I have recreated everything exactly in storyboard, and it is not scrolling. I'll edit my question with some screenshots.

So it turns out my attempt to recreate the code in storyboard wasn't 100% correct. The trailing value for the last button should be exactly zero, mirroring H:|-[buttonA]-[buttonB]-[buttonC]-| which isn't very obvious when working in storyboard (or at least wasn't to me) but now after the fact seems trivial. I love autolayout but those little things get ya...

So, I set the constraints between the scrollView and the buttons, and not the containerView and the buttons? I think that's about the only difference I am sporting betwen your code and what I am doing via storyboard right now.

That will set the scrollView to fill the size of the containerView (so the containerView will have to be of a certain size).

The contentSize is implicitly set by applying the constraints inside of the UIScrollView.

What @user1459524 mentioned was an issue for me. My bottom view was a button and the bottom constraint was -32 by IB. I set that to 0 and it updated in IB to be Bottom Space to: superview. It will work, also it seems as long as you are on the positive side it works. Looks like that may be a bug with IB. Hope that helps someone else. I also removed all views and rewired which helps when trying to lay these out I find and it doesn't usually take long.

You can then adjust the contentSize of the UIScrollView by implicitly setting it to be large enough to hold the buttons like this:

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


Ah, yes. The negative value would actually make the contentSize of the scroll view smaller, to match the fact that the trailing edge should end before the button ends. Glad the sample project helped!

Also of note, as others mentioned, with AutoLayout and UIScrollview, you no longer set the contentSize explicitly. The AutoLayout System calculates the contentSize based on your constraints.

I also found this ebook chapter to be very helpful in making me understand how all this works. Hope all this helps.

I also had a lot of trouble originally getting Auto Layout to work with UIScrollView. The key to getting it to work is making sure that all of the items in the scroll view, taken together, have constraints that eventually link to all sides of the scroll view and that contribute to the AutoLayout system being able to determine a contentSize for the scroll view that will be bigger than its frame. It looks like you were trying to do that in your code, but maybe you had some superfluous constraints in there that were making the contentSize too small.

In lieu of an explanation of what's wrong in your setup, I've created a basic sample project with a very similar view hierarchy and constraint setup to the one you describe. The horizontal scrolling works as expected in the sample project, which uses the "Pure AutoLayout" approach that Apple describes in the Technical Note.

It did a lot. Check out my updated answer. The trailing value should actually be 0 (zero) it seems.

It's hard to see the exact values and setup of your constraints as you've pasted them here, so I'm not sure from looking at your screenshots where you have gone wrong.

THANK YOU for the sample project! It works, and while looking through it, I couldn't find any difference to my project at first! I copied the views into my project, suspecting some other setting than constraints, but it continued to work. This led me to notice that the trailing constraint for the last button in my project has a negative - in front of it, while yours has a positive value! Changing the - to positive enabled the scroll! When I bring up the Pin toolbar, there are two choices: Use Current Canvas value, which is negative, and Scroll View (use current value) which is positive...?!

That is sad thing we have to share screenshots instead of code. That is just because apple encourages developers to use builder instead of providing better api.

Totally unintuitive. Developers have to squander hours and hours to figure what was going on on XCodes creators mind.

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


UIView
   - ScrollView
    - Subview
    - Subview
    - Subview
    - Subview
    - ...

Also, how did you make your superview so much longer than the default length? I'm trying to add buttons that will appear "below the fold" (or off screen) but obviously I cant add them to the superview if I cannot drag and drop them onto the superview since it's not big enough.

For VERTICAL scrolling: the only way I could get it to work (iOS 8, Xcode 6 and pure autolayout) was adding the following constraints to my Scroll View (all related to the superview):

Greetings! That "Subview" is actually an UIVIew. That is just a name... You can type anything there. Check out this image: cl.ly/image/291f1K2N0V3p On the constraint topic, the subviews have more than just a height constraint. Check the entire constraint list for those views here: cl.ly/image/3l061i123j2i

Hopefully this would save someone from GOING TO SLEEP AT 5 AM. :D

I'm still not able to get scrolling working -- I noticed that your subviews views are "greyed out" in your project. After doing some research, it looks like this is because you are using 'size classes.' How does this affect the scrolling? Is there a way to get the scrolling working without using this?

LOL welcome to the stupidity club. I'm one of the founders. :D

Thanks a lot for the help! How did you get the subview of type "subview" ?? I'm looking at your project in Xcode 6.2, and it calls those little rectanges "Subview" with the only constraint being that it's 63px high.

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


Here's a simple example. Create a storyboard with one view, that has one scroll view. Set that scroll views constraints to make it fit the size of the view you put it in.

If you have multiple views you want to add to a scroll view, for example laid out horizontally, you'd lock the left side of the first subview to the left of the scroll view, lock the subviews to each other horizontally, and the right side of the last sub view to the right side of the scroll view. Those constraints would force the content size of the scroll view to expand to accommodate all of the subviews and their constraints.

Inside that scroll view add a single view. Explicitly set the size of that view using constraints (and make sure that size is bigger than the scroll view).

Now add four more constraints to that inner view locking the four edges of the inner view to its parent scroll view. Those four constraints will cause the content size to expand to accommodate the inner view.

There is a piece in the tech notes that you may have looked over. You can implicitly set the content size of a scroll view using constraints fixed to the edges of the scroll view.

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


ContainerView
Labels
ScrollView
ViewControllerView
Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


as you normally would

scrollView with autolayout and without contentSize

  • Critical. After you set the height to some large value in point (3), go back to point (2) and be certain to set the top, left, right, bottom values back to zero as Xcode may have changed them for you when you force changed the height in (3).
  • Drag n drop a UIView inside the scrollView and make it cover the whole space of scrollView and apply constraints to be top, left, right, bottom space from scrollView.
  • Drag n drop a scrollView to viewController and apply whatever constraints to cover the space you want.
  • Set the height (and width if horizontal scrolling is required) of the inner view as per the need of scrolling. This part can also be done from code if required.

And you're done. Now, you can add any number of controls on this view and apply the constraints relevant to each other (which don't seem working without this view). If you don't want to use this view then you'll have to apply constraints for each control related to scrollView (not related to each other).

Critical. Let's say for clarity the UIScrollView is 1000 wide and 100 high. (In fact normally these values would be dynamic, of course, depending on the width of the device etc. But for now just say 1000 wide and 100 high.) Let's say you are doing a horizontal scroll. So put a UIView inside the UIScrollView. (That is the "content view".) Set all four constraints of the content view top, bottom, leading, trailing, to the scroll view. Make them all zero even if that seems wrong. Set the height of the content UIView to 100 and forget about that. Now: you want to scroll horizontally, so set the width of the content view to be let's say 1225.

Edited: I've ended up with using UITableView instead of UIScrollView for most of my requirement. As tableView seems to me much more flexible and dynamic.

Good work, it helped me a lot.

Interestingly, you can actually set the leading/trailing numbers to any positive value (try say "50") and it gives you kind of a margin of the bounce. (It often looks great: try it.) Any negative value on either end will "silently break".

Note that the width of the content view is now 225 bigger than the width of the parent scroll view. That's OK: in fact, you MUST do that. Note that

Note that, infuriatingly, often Xcode (as of 7.3.1 anyway),

because it tries to automatically tally them for you. If so it will silently break. Set all four values to zero in the first instance. And set the width of the content view much wider than the "1000" in the example.

Note
Rectangle 27 1

ios How can I use Autolayout to set constraints on my UIScrollview?


@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewWidthConstraint; 
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewHeightConstraint;
CGSize viewSize = self.view.frame.size;
self.contentViewWidthConstraint.constant = viewSize.width;
self.contentViewHeightConstraint.constant = viewSize.height;

After some time dealing with this issue, I finally found a solution. I'm working with universal class sizes storyboards (600x600). I created a UIView (contentView) the size of the scrollView and created constraints to Top, Bottom, Leading and Trailing to the scrollView. Then I clipped the size manually of the contentView to 600x600. The storyboard stopped trying to resize everything and I could work but the view looked awful on the real device or simulator. I made 2 constraint outlets of this clipped sizes.

Then in viewDidLoad

Note