UIScrollView: How to make AutoLayout work on it, IN CODE
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:
Initially, things look straight and easy to me, just:
- Add an
UIScrollView
to mainUIView
. - Add top, left, right, bottom AutoLayout constraints on
UIScrollView
. - Add an
UILabel
(with lots, lots of text displaying) as subview toUIScrollView
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:
- 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:
- We setup all these AutoLayout constraints in
viewDidLoad
. - Add
UIScrollView
property as the subview of main view of thisUIViewController
, and top/left/right/bottom constraints to it as well. - Add an
UIView
, known ascontentView
of thisscrollView
, as the subview ofscrollView
, and its top/left/right/bottom constraints. - KEY: since the
UIScrollView
has been added withsafeAreaLayoutGuide
, Itโs not quite necessary to add its subview withsafeAreaLayoutGuide
. If you addedsafeAreaLayoutGuide
totopAnchor
orbottomAnchor
, it would not work. - KEY: Set the width of
contentView
equal to the main view actually helps thescrollView
to setup itscontentSize
, since ifcontentSize
is not setup properly,UIScrollView
will not work(scroll/zoom). - Add first
UILabel
as subview ofcontentView
with top/left/right/bottom constraints. - Add second
UILabel
as subview ofcontentView
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.