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
UIScrollViewwith 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
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,
- Ability to add any view as a sub-view to it and our
scrollViewshould be able to resize vertically to accommodate these added views (I have intentionally left out horizontal scrolling for the sake of simplicity)
- Ability to specify padding in vertical direction
- 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.
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
// 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
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.
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)
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) ])
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!