UIScrollView: How to make AutoLayout work on it, IN CODE

Yi ๐Ÿ๐Ÿ
4 min readMar 13, 2018

--

Before you read this article, but have no idea about UIScrollView, I will strongly suggest you to read this introduction article on UIScrollView.

PS: Ever since I started Brian Voongโ€™s Youtube Course on Youtube Course, I already had converted my to be programmatical coder, which means I wrote 100% of my UI staff in code, rather than storyboard.

First thing first:

Preview

Initially, things look straight and easy to me, just:

  • Add an UIScrollView to main UIView.
  • Add top, left, right, bottom AutoLayout constraints on UIScrollView.
  • Add an UILabel (with lots, lots of text displaying) as subview to UIScrollView just added.
  • Add top, left, right, bottom AutoLayout constraints on this newly added UILabel.

Then, I thought things should be working. While, thatโ€™s not the case.

Issue: The UIScrollView doesnโ€™t scroll.

Cause: The contentSize of UIScrollView has NOT been properly set.

How to do it in the right way:

  1. In the existing or newly created project, create a new Swift file: ScrollViewController.swift

2. In ScrollViewController, add all the UI elements will be used soon.

let text =  """Lorem ipsum dolor sit amet, in alia adhuc aperiri nam. Movet scripta tractatos cu eum, sale commodo meliore ea eam, per commodo atomorum ea. Unum graeci iriure nec an, ea sit habeo movet electram. Id eius assum persius pro, id cum falli accusam. Has eu fierent partiendo, doming expetenda interesset cu mel, tempor possit vocent in nam. Iusto tollit ad duo, est at vidit vivendo liberavisse, vide munere nonumy sed ex.                                                Quod possit expetendis id qui, consequat vituperata ad eam. Per cu elit latine vivendum. Ei sit nullam aliquam, an ferri epicuri quo. Ex vim tibique accumsan erroribus. In per libris verear adipiscing. Purto aliquid lobortis ea quo, ea utinam oportere qui.                                                """   

fileprivate let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = .green
return scrollView
}()
fileprivate let contentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .purple
return view
}()
fileprivate let titleLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
label.font = UIFont.preferredFont(forTextStyle: .headline)
label.textAlignment = .left
label.backgroundColor = .yellow
label.text = "App"
return label }()
fileprivate let appDisplay: UILabel = {
let textView = UILabel()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.backgroundColor = .cyan
textView.numberOfLines = 0
textView.font = UIFont.preferredFont(forTextStyle: .body)
return textView
}()

In the above code, we added a UIScrollView, a contentView of UIView, and two UILabels. Just like the contentView in UITableViewCell, this contentView will be the parent view of all the display views. In this example, we use two UILabels. The reason behind it is it will be much easier to manage all the display viewsโ€™ positions, transitions and other UI related staffs. So, we specifically give each view a distinguishable background color, thus the final demo could easily identify the views.

3. Apply AutoLayout to the views.

override func viewDidLoad() {        
super.viewDidLoad()
// 1
self.setupViews()
}

fileprivate func setupViews() {
// 2
self.view.addSubview(self.scrollView)
self.scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
self.scrollView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor).isActive = true
self.scrollView.leftAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.leftAnchor).isActive = true
self.scrollView.rightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.rightAnchor).isActive = true
// 3
self.scrollView.addSubview(self.contentView)
// 4
self.contentView.topAnchor.constraint(equalTo: self.scrollView.topAnchor).isActive = true
self.contentView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor).isActive = true
self.contentView.leftAnchor.constraint(equalTo: self.scrollView.safeAreaLayoutGuide.leftAnchor).isActive = true
self.contentView.rightAnchor.constraint(equalTo: self.scrollView.safeAreaLayoutGuide.rightAnchor).isActive = true
// 5
self.contentView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
// 6
self.contentView.addSubview(self.titleLabel)
self.titleLabel.topAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.topAnchor).isActive = true
self.titleLabel.leftAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.leftAnchor).isActive = true
self.titleLabel.rightAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.rightAnchor).isActive = true
self.titleLabel.heightAnchor.constraint(equalToConstant: 80).isActive = true
// 7
self.contentView.addSubview(self.appDisplay)
self.appDisplay.text = self.text + self.text + self.text + self.text + self.text + self.text + self.text + self.text + self.text + self.text + self.text + self.text + self.text
self.appDisplay.topAnchor.constraint(equalTo: self.titleLabel.safeAreaLayoutGuide.bottomAnchor, constant: 16).isActive = true
self.appDisplay.leftAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.leftAnchor).isActive = true
self.appDisplay.rightAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.rightAnchor).isActive = true
self.appDisplay.bottomAnchor.constraint(equalTo: self.contentView.safeAreaLayoutGuide.bottomAnchor).isActive = true }

In the above code, we could see:

  1. We setup all these AutoLayout constraints in viewDidLoad.
  2. Add UIScrollView property as the subview of main view of this UIViewController , and top/left/right/bottom constraints to it as well.
  3. Add an UIView, known as contentView of this scrollView, as the subview of scrollView, and its top/left/right/bottom constraints.
  4. KEY: since the UIScrollView has been added with safeAreaLayoutGuide, Itโ€™s not quite necessary to add its subview with safeAreaLayoutGuide. If you added safeAreaLayoutGuide to topAnchor or bottomAnchor, it would not work.
  5. KEY: Set the width of contentView equal to the main view actually helps the scrollView to setup its contentSize, since if contentSize is not setup properly, UIScrollView will not work(scroll/zoom).
  6. Add first UILabel as subview of contentView with top/left/right/bottom constraints.
  7. Add second UILabel as subview of contentView with top/left/right/bottom constraints.

As you might realized, the Key steps to make UIScrollView work with AutoLayout is One Must-Do: Step 5, and One Must-Not-Do: Step 4.

For Step 5, you need to somehow setup the right contentSize of UIScrollView, otherwise it will just not work, since without this property being set, it just have no idea how to move or zoom, which just totally makes sense.

For Step 4, actually I am not quite clear why is that. I only made it work after hours of debugging process, and it had been tested on iPhone X.

Thank you for reading, if you have any questions or answers to my problem, please kindly leave a comment.

--

--

Yi ๐Ÿ๐Ÿ
Yi ๐Ÿ๐Ÿ

Written by Yi ๐Ÿ๐Ÿ

Write the Code, Change the World.

No responses yet