I’ve been using NSLayoutAnchor class to define constraints on my UIView controls. But when working with UIScrollView i got stuck. It was not scrolling down or up as i add new subViews in it. As it turns out, you need to calculate the spacing between the elements in scrollView, yourself.

Top Anchor must be set to ScrollViews'

I have a container UIView controller and it’s constraints are set to the safeArea like this:

let margins = self.view.safeAreaLayoutGuide

let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(containerView)

I use this container because for leading and trailing constraints for my subViews, i will use this container’s anchors. Let’s define the constraints for it:

containerView.topAnchor.constraint(equalTo: margins.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: margins.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: margins.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: margins.trailingAnchor).isActive = true

Initialize UIScrollView instance

ContentSize of scrollView must be set, otherwise it might not be scrolling your elements. We also need to add constraints for it, too;

let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.contentSize = CGSize(UIScreen.main.bounds.width, height: 1400)
containerView.addSubview(scrollView)

scrollView.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true

We are done with our scrollView, now we need to add a few elements to it.

Add random UIView elements


var fixedHeightOfLabel : CGFloat = 30

for index in 0...18 {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints = false
    label.text = "Current Label: \(index)"

    scrollView.addSubview(label)

    label.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: CGFloat(index) * fixedHeightOfLabel).isActive = true // we calculate the distance from the top of scrollView, ourselves

    label.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
    label.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
}

As you see we need to calculate the distance from the top of scrollView. In another case, let’s say you wanna add your label’s topAnchor right to the bottom of the previous label, scrollView won’t scroll. That is why we calculate that distance.

Also leading and trailing anchors are set to the containerViews'. That is another must, cuz if you set them to the scrollViews', then it won’t scroll your labels again.

Conclusion

You can combine all the code and add it to your viewDidLoad(), it’s your choice.

To sum it up,

  • Always make sure you provide your UIScrollView a contentSize value,
  • Do not set subViews leading or trailing anchors, to the scrollViews'
  • Calculate the distance yourselves according to topAnchor of scrollView.

And you should be good to go!