Using UIScrollView with Autolayout on Xcode using Swift

Note : This post is based on Using UIScrollView with Auto Layout in iOS published on Atomic Object blog.
TL; DR; The code to support UIScrollView with autolayout is open-sourced and hosted on Github. The wrapper can be found here and example on how to use it is listed here

Recently I read an article about using UIScrollView with Autolayout on iOS platform. I was quite fascinated by it as my experience using both of them together has always been terrible. After reading an article, I thought, why not make this even easier by making a wrapper? I that's how this post came to life.

To facilitate using any UIScrollView with autolayout, I built a wrapper so that anyone should be able to make it work without worrying about underlying constraints and set up associated with this combination.

Let's call this wrapper a ScrollViewAutolayoutCreator. We will be supporting following features to it,

  1. Ability to add any view as a sub-view to it and our scrollView should be able to resize vertically to accommodate these added views (I have intentionally left out horizontal scrolling for the sake of simplicity)
  2. Ability to specify padding in vertical direction
  3. Being able to scroll to all the way vertically in both directions

First, let's begin by writing a wrapper which can allow us to easily add vertical scrolling support for arbitrary number of views with any spacing between them. Let's call this wrapper a ScrollViewAutolayoutCreator. You can find the full source code for wrapper here.

Initializer of ScrollViewAutolayoutCreator just takes one parameter namely superView. This is the view to which we want to add our UIScrollView subclass as a subview.

Now, let's create an instance of ScrollViewAutolayoutCreator.

// We are using `self.view` of a current viewController as a superView
let autolayoutScrollView = ScrollViewAutolayoutCreator(superView: self.view)

ScrollViewAutolayoutCreator has a property namely contentView to which we will add required subviews.

Let's create some sample views and add them as subviews to autolayoutScrollView.contentView.

let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0

label.text = "hello hi buddy\nadadasd\nadadasdas d sd asd asd \na da d adas d\nasdasd"
label.backgroundColor = .green

let sampleView = UIView()
sampleView.translatesAutoresizingMaskIntoConstraints = false
sampleView.backgroundColor = .red

let bottomLabel = UILabel()
bottomLabel.translatesAutoresizingMaskIntoConstraints = false
bottomLabel.numberOfLines = 0

bottomLabel.text = "hello hi buddy\nadadasd\nadadasdas d sd asd asd \na da d adas d\nasdasd"
bottomLabel.backgroundColor = .yellow

let contentView = autolayoutScrollView.contentView

contentView.addSubview(label)
contentView.addSubview(sampleView)
contentView.addSubview(bottomLabel)

Now let's add missing constraints to our sub-views.

Horizontal constraints:

Adding horizontal constraints is as simple as constraining left and right anchors to respective anchors on super-view. However, as long as we have list of all the sub-views, the wrapper provides a way to automatically add them with padding.

// Attach horizontal Constraints
autolayoutScrollView.addHorizontalConstraints(views: [label, sampleView, bottomLabel], horizontalPadding: 20.0)

Vertical constraints:

This is slightly more complicated as we have a vertically scrolling view, thus we want to make sure all the constraints between views as well as those on top and bottom are correctly set up. Here too wrapper makes it as simple as just passing an array of UIViews and padding between all the views.

// Attach vertical Constraints
autolayoutScrollView.addVerticalConstraints(views: [label, sampleView, bottomLabel], verticalPadding: 100.0)
Note: It is important to note that usage of two above mentioned methods is completely optional. Provided methods add generic constraints with uniform padding. However, there could be cases when you want to add custom constraints in either directions. In which case you don't have to call them

For example, if you decide to have custom constraints in vertical direction, your code might look like this,

NSLayoutConstraint.activate([
    label.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20.0),
    sampleView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 400.0),
    bottomLabel.topAnchor.constraint(equalTo: sampleView.bottomAnchor, constant: 400.0),
    bottomLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20)
    ])

Bonus methods:

The wrapper provides two more bonus methods to scroll all the way to the top and bottom

func scrollToTop()
func scrollToBottom()

This is it for this post. Below is the video with demo how this whole setup looks like on the simulator.

This is my very first attempt to re-write this wrapper from Objective-C into Swift. If you see any issues with the implementation or have a suggestion for improvement, I would love to hear from you! Thanks for reading!