iOS: How To Make AutoLayout Work On A ScrollView

Ok, I’ll admit. I’ve been seriously struggling with AutoLayout ever since it’s been introduced. I understand the concept, and I LOVE the idea of it, but when I actually do it, it almost never behaves as it does in my head.

So when I had a chance to go talk to an actual Apple Engineer about AutoLayout last week at WWDC, I made sure to go. I thought of my most painful experience using AutoLayout recently – when I was making a login screen with username and password fields on a ScrollView (so it scrolls up when the keyboard comes up) – and had the Apple engineer walk me through the example.

Here is what we made:

ScrollView TextFields Autolayout ScrollView AutoLayout 5

This is just two input fields centered on a ScrollView. You can see the AutoLayout at work here – the two input fields are centered correctly both on a 4s and a 5s device.

This “simple” solution took the Apple Engineer 40 minutes to solve! However, several senior engineers I know said that they’ve never been able to get AutoLayout working quite right on a ScrollView, so 40 minutes is actually not bad!

Here are the key tricks to getting AutoLayout to work on a ScrollView:

One View

The ScrollView should have only ONE child view. This is forced in Android, so I should have made the connection, but I just didn’t think of it – it’s too easy to put the two input text fields right onto the ScrollView, especially in Interface Builder.

Here is what the View Hierarchy should actually look like:

scrollview hierarchy

Again, make sure to put all your fields and custom views inside the one child view of the ScrollView!

Equal Widths

I’m going to start with the constraints from the outside (on the main view) in (to the stuff inside the ContentView).

The obvious starting point is to bind the ScrollView to the View – just select the ScrollView from the view hierarchy, and add the following constraints:

constraints

The key to getting the constraints to work properly however, is adding an Equal Width constraint between the main View and the ContentView. The ScrollView adjusts to the size of the content inside of it, so setting the ContentView to the Width of the ScrollView leaves the width of the ContentView ambiguous.

To create the Equal Width Constraint between the ContentView and the View, select the ContentView on the view hierarchy and Control + Drag to the View – you should get a pop-up that gives you the “Equal Widths” option:

equal width option

Your constraints between the main View, ScrollView, and ContentView should look like this:

Equal Widths

Content Insets

The constraints between the ScrollView and the ContentView are surprisingly straight forward – just bind the ContentView to the ScrollView (make sure the constant to the bottom layout guide is 0):

constraints

The constraints between the ContentView and ScrollView are now as follows with all constants set at 0:

scrollview to contentview

If your storyboard is like mine, you might notice that the actual ContentView is not the full height of the main view or the ScrollView:

storyboard views

However, we do want to make sure the ContentView is centered when it’s rendered on a device. To do that we need to write some code to property set the Content Insets in the ViewController:

// ViewController.swift

import UIKit

class ViewController: UIViewController {
                            
    @IBOutlet var scrollView : UIScrollView
    @IBOutlet var contentView : UIView
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func viewDidLayoutSubviews()
    {
        let scrollViewBounds = scrollView.bounds
        let containerViewBounds = contentView.bounds
        
        var scrollViewInsets = UIEdgeInsetsZero
        scrollViewInsets.top = scrollViewBounds.size.height/2.0;
        scrollViewInsets.top -= contentView.bounds.size.height/2.0;
        
        scrollViewInsets.bottom = scrollViewBounds.size.height/2.0
        scrollViewInsets.bottom -= contentView.bounds.size.height/2.0;
        scrollViewInsets.bottom += 1
        
        scrollView.contentInset = scrollViewInsets
    }


}

Once you add the proper constraints into the ContentView (see next step), your final result will look like this:

centered container view

The ugly colors are meant to differentiate the ScrollView (green) from the ContentView (red). Again, in the storyboard, the ContentView is at the top of the ScrollView, but with our content insets set in code, it now becomes centered.

Centering Multiple Views

The final step is to add AutoLayout to the ContentView. This is the same as adding layout normally to any view, so I won’t go into much detail here.

The one thing I did learn that I’d like to share (although now it seems obvious) is how to center the two text fields in the view. Previously, I put the two text fields into a container view, and centered the container view in the parent view. However, that is not necessary.

Instead, you can center each text field horizontally in container (so they’re now centered and on top of each other), and then add a constant of 25 to one (so it’s moved up 25 pixels from the center), and add a constant of -25 to the other (so it’s moved down 25 pixels from the center).

top 25 bottom -25

This will leave you with a space of 50 pixels between the two text fields, but the space exactly in between them will be the center of the view.

Do you have any other AutoLayout tips? I’m always looking to learn more and improve, so please let me know in the comments!

You can view the source code on Github here.

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • nice

  • Very helpful. For anyone using nibs, you won’t be able to set the “Equal Width” constraint on the main view. Instead, create another top-level UIView that contains your scrollview.

    Set its Top,Bottom,Leading,Trailing to superview and link your existing contentView to the new view’s width

    • Mat Cegiela

      In a xib situation, as a workaround, you can also create a second top-level view and drop your view into it. This allows you to link constraints to it form deeper level views. After you create all the constraints, simply lift it out again and delete the temporary containing top-level view.

    • Benjamin Horner

      Wow ! Thanks !!!! you saved me a headache !!! I was going crazy with the iPhone 6 screen size !

    • Tim Smith

      This helped me hugely, but for clarity the ScrollView should not be contained in the new view .. otherwise you still don’t get the equal widths option.

  • tomqz

    Super helpful! I was looking for something like this very long. No body used the trick with equal widths. Thanks a lot! 🙂

  • Elviin

    Nesting the content view is nice. But it is also handy to just place the child components in the scroll view (any number of them), to setup the specific height and width constraints for each of them and manipulate the constraints (constant property) later in the code which causes updating the scroll view content without any hard work. Also binding a width/height to the nested view from a superview(main view) is not always possible.

  • Jeff

    Super helpful, thanks a lot for sharing!

  • Perry

    Perfect solution… Only one that I have found and worked. Thanks man

  • Jochen Bedersdorfer

    After hours of frustration with Scroll View this article restored my sanity.
    THANK YOU!
    (note that I also had to turn off Adjust Scroll View insets on the view controller)

  • Ronn Lixx

    Helpful indeed. Auto Layout, constraints, ScrollViews have me smashing keebords like a monkey. You say “The ScrollView should have only ONE child view.” Is this your opinion, or is this something your apple contacts told you to be the case? Regardless why should a scrollview only have one child view?

  • Gugulethu Mhlanga

    VerY helpful. Any Ideas on how to create a horizontal scrollview?

  • RK

    Thanks for this usefull explanation but … on my iPhone 6+ with iOS8.1, when the keyboard is open, it is not possible to scroll in the view (impossible to see the textfield). What’s wrong ? I have used our source code from Github. Any help will be great.

    • Tommy Juszczyk

      Also seeing this in 9.1

  • Matthew Martindale

    2 evenings spent on this subject before I stumbled upon this site and this sentence:

    “The key to getting the constraints to work properly however, is adding an Equal Width constraint between the main View and the ContentView.”

    It feels like I’ve discovered fire.

    • Guest

      Reference:

      • Tuan Anh Vu

        LOL. Yes.

    • Eric Miller

      Exactly the same experience. That line is magical… If you want your scrollview content to resize with your device/orientation.

    • evian

      The equal width constraint can actually just go from the content view to the scroll view and not to the main view. Not sure if something changed from when this article was written or not.

  • ximmyxiao

    I just do as the list told ,After set Equal Width ,but the IB still complains about has ambiguous scrollable content height , how should I set the correct scrollable content height

  • After a couple of frustrating hours, this is the solution that finally worked and made complete sense. Well done and thanks!

  • hotPotato Dev

    Wonderful! The trick for me was adding the content view inside the scroll view

  • Chris

    Thanks for posting this. Making sure that there was only one child of scrollview was the key for me.

  • nobody

    “The ScrollView adjusts to the size of the content inside of it, so setting the ContentView to the Width of the ScrollView leaves the width of the ContentView ambiguous.” – that is strange because if I set equal width between scrollview and contentview then it also works in the same way. Contentview width is properly taken from scrollview and I do not have ambiguous layout.

    • Aslı Sabancı

      Same here. Adding “Equal Widths – ContentView – Scroll View” constraint works as well, instead of adding “Equal Widths – ContentView – View” constraint.

      • bonio

        Yes it seems that adding “Equal Widths – ContentView – Scroll View” works properly now however I’m pretty sure some time ago it did not work this way. Possibly something was changed in some later iOS or Xcode version.

  • Vassilis Aggelakos

    I give up! This is really messy and I prefer to write some code to place my controls where I want in every situation instead of this sh*t! AutoLayout is seriously broken! Very often you are ended to write code to set the correct values to your own constraints! IMHO, Auto Layout is a good idea but it is implemented furiously. It seems like it is in beta version yet!!!

    • BCichowlas

      I almost gave up on it for all of the reasons you mention, but eventually this worked out for me and I understand it well enough to be able to do it again. I found it helpful to make a very simple test project with the scroll view in the same place that I wanted it in the actual project.

  • salihamad

    Amazing! This really solved the issue I was having. Thanks!

  • CodeNewb

    Ok, what in the heck is a ContentView… I have a View, I have a Container View, I have a Web View but no Container View option in xCode… help.

    • It’s just a UIView.

      • BCichowlas

        But it can be a container view and that’s generally what I do. That way it is in a separate scene on the storyboard, which makes it very easy to set up its contents.

  • wLcDesigns

    Very helpful, thank you.

  • James Clutterbuck

    The “scrollview has ambiguous scrollable content height” fix for me was to add a constraint on my last element for bottom spacing to ContentView and all the other elements in the ContentView have constraints on their Top/Bottom to the element Above/Below them, giving a finite height and no more warnings 🙂

    • I think this is a big reason why scroll view does not work for lots of people. Setting the spacing between elements gives the scrollview a fixed height.

      This is how I got success for my scrollview. Thanks for mentioning it here!

    • Scotty Fister

      This did it for me too, thanks! Also ensuring that my scrollview/content view had constraints relative to the superview – it kept trying to use “bottom layout guide” and that screwed it up, too. Clicking the carot when setting pin values you can check view instead of layout guide.

    • Dan

      Great solution, I think a lot of people can’t figure out to do dynamic scrollviews but this is the solution. Thanks!

  • Enrique

    Saved my live!

  • ericwastaken

    Margins in IOS8/newer XCode break these instructions a bit because child objects are offset by 8 in all around the parent. There seem to be several steps to resolve this. One is you want to uncheck the “Constrain to Margins” when adding new constraints. Alternatively, if you’ve already added some constraints and didn’t realize to do this, you can later edit the constraint and in the drop down for “Second Item”, you’ll want to uncheck “Relative to Margins”. Additionally, if you chose to keep the relative to margins (it is convenient when nesting) when setting the WIDTH of the ContentView to the width of the parent view (not the scroll view), the parent will be 16 larger (8 on either side). Therefore use a “-16” in the Constant for the “Equal Width” constraint. There’s a really good article explaining these margins here: https://stackoverflow.com/questions/25807545/what-is-constrain-to-margin-in-storyboard-in-xcode-6

  • WilfredGreyling

    My hat! “Equal Width” did the trick. Thanks so much for putting this out there.

  • Remi Hopkins

    The source on github doesnt work. It doesn’t dismiss the keyboard nor does it scroll when the keyboard appears…

  • Amit Thakur

    very helpful.

  • Marc Baumgartner

    Thank you very much for this great article! After hours of struggeling with the scrollpane this saved me!

  • LightRider

    Well, it took 40 mins by an Apple Engineer to do such a simple thing means this really is not a good disign

  • Anas Mostefaoui

    you save my day !
    i was wondiring why on earth, my view, does not respect my width constraint !
    thanks a lot !

  • James Tien

    Thanks! This thorough walkthrough saved my day! Also, I don’t feel bad anymore that I couldn’t get it work for 4 hours.

  • Joe

    I’m not sure if they changed something, but adding constraints between the main view of a nib and any subviews within a UIScrollView is disabled. This appears to be the case with nibs, but not with storyboards.

  • Only problem now is that all my elements inside the content view can’t be interacted with, i.e. they do not respond to user events. Any workaround or idea why?

    • Eidan

      I have the exact same problem. For example a UIButton that you need to scroll down in order to view cannot be interacted with.

  • Thomas Tempelmann

    I tried to do this in Xcode 6.4 but it would not let me connect the main view with the content view. It seems like it doesn’t want to let me reach over two levels, as I could connec the contenview to the scrollview. Bug or feature? Anyway. I then instead add the equal-width constrains in code (inside viewDidLoad) and that solved it just as well. Only drawback is that somehow Xcode now believes the content view shall be 1873×1425, which makes editing the view a bit difficult. But when running the app, all is fine.

  • vedpati tripathi

    keep helping with your useful tutorials…..
    i learnt it..

  • Pritam Salunkhe

    Awesome…It’s Very easy..

    Thanks for very usefull explaination

    Now “scrollview has ambiguous scrollable content height” never fight with me

  • Michael Caraccio

    Your post helps me a lot! Thanks!

  • vincent

    My layout follows this guide (great tutorial!) and it all works well, but in run time the content view is about 2 or 3 points less than the width of the view (thereby the scroll view as well) causing it to not reach the right side flush… i’ve gone through all my constraints and can’t find a reason why this is happening. Any tips would be appreciated!

  • Fustigador

    This “doing easy things make difficult” is why I HATE Apple passionately. Your post was helpful, thanks!

  • Fustigador

    Can’t I say I hate Apple? Do I have to be a mindless zombie that nods everything they do, even if it is done with the ass?

  • Tommy Juszczyk

    Honestly never thought I would ever live to see the day that I implement a scrollview in autolayout.. thank you

  • Vitor Oliveira

    Amazing article! It really helped me. I forgot to set the equal width constraint. Thanks!!

  • Jimbo

    Thank you, thank you! I wanted a header and a footer to a implement a pull to refresh for my entire view, however, scrollView was not cooperating. Now that I put the header and footer inside a Content View and set the simple constraints you specified, it works!! Note: For others trying to implement pull to refresh with a scrollView, make the ViewController a UIScrollViewDelegate and you will likely want to implement scrollViewDidScroll() and scrollViewDidEndDragging().

  • Jônatas

    Great article!

  • IanKay

    Nice article, I’ll have to give this method a go. I wrote this a long time ago and have always used it to use autolayout in scrollviews – no extra constraints required; you can just design your UI like you always have
    https://gist.github.com/IanKeen/2a41e9f55311af1c20f2

  • Hi Natasha! Thanks for this info. I know this article is almost two years old now, but maybe my comments will help somebody else in the future.

    I was having a lot of trouble trying to use stack views in a scroll view and found this article, which helped me out.

    One thing I want to say is that I was actually able to figure this out without having to make an equal width constraint between the main view and the ContentView as you mentioned to being key to the solution. My project had several stack views (each stack view for an image and description) all within a main stack view, with the main stack view in the scroll view, and the scroll view within a regular UIView (to be able to put a nice thin border around it, inset from the window a little bit to look nice). The main constraints were to constrain the main stack view to the scroll view, and to constrain the scroll view to the container view UIView. I’m a little new at this (about 5 months or so now), so I’ll put a link to my project on GitHub if you would like to see how I did it. I realize I may have put more constraints than was necessary, but I don’t get any red or yellow constraint errors/warnings in Interface Builder, and no errors in the debug output pane when it is running (which I’ve seen often when trying to figure out how to use scroll views with auto layout), so I’m happy about that. Thanks again!

    Project on GitHub: https://github.com/billy70/favoriteplace

  • I owe you a drink if we ever meet. This just solved a great deal of head scratching! Thank you :))

  • BCichowlas

    This was very, very helpful. I was only worried that it was obsolete for Xcode 7, considering how Apple changes things so frequently. For me, the real moral is that most views generally need 4 constraints, but the view contained within a scroll view generally needs 6. If you think in terms of the degrees of freedom, it makes sense. Now that Xcode has “Embed in scroll view”, I like to get started by adding the content view to the storyboard and then doing “embed in scroll view” to put the scroll view around it.

  • Lasse Bunk

    Thanks for posting this. It’s nice to get an Apple engineer to actually say that you have to write code to do this. So now we know what to do. Thanks again 🙂

  • realscottnelson

    Very helpful. For my storyboard view, I actually got it to work with two views inside my main content view (which was inside the ScrollView). I also didn’t set the width equal to the View, but instead set the leading and trailing space to the View.

  • Kevin

    It seems like “containerViewBounds” isn’t being used anywhere

  • Iain Munro

    Thanks for the details.

    One thing that I think is missing from all the discussions is a real life example. For example, I have an iPhone 5 setup just looking perfectly.

    After some messing around, I managed to get everything looking not too bad on the iPhone 6 plus – but it is no where where it should be. Instead of constraints, I used autolayout, but when on the iPhone 6 plus, there is still a lot of wasted space and buttons that we looking good on the iPhone 5, now jus have some much room between them.

    Is there a way to have it looking nice on the iPhone 5, then do some moving around of the items in the iPhone 6 plus size ?

  • Wojciech Bilicki

    Finally was able to set this properly thanks to your post!