Stéphane de Luca Stéphane de Luca - 7 months ago 17
Swift Question

Cannot create UIScrollerView with pure auto layout constraints

I stripped down my code so that its easy to understand.

Say you have a controller and you want to add a simple scroller using pure auto layout.

You can invoke my function tool (provided below) as follows:

// Create scroll view
let strip = addStripCategoryTo(view)

// Attach it to the view, vertically and horizontally
strip.topAnchor.constraintEqualToAnchor(view.topAnchor).active = true
strip.leftAnchor.constraintEqualToAnchor(view.leftAnchor).active = true


// The function
func addStripCategoryTo(parent: UIView) -> UIView {

let h:CGFloat = 128
let w = 2*h/3
let n = 5
let width = w * CGFloat(n)
let height = h

// Scroll view
let scrollview = UIScrollView()
parent.addSubview(scrollview)

scrollview.scrollEnabled = true
scrollview.translatesAutoresizingMaskIntoConstraints = false
scrollview.widthAnchor .constraintEqualToAnchor(parent.widthAnchor).active = true
scrollview.heightAnchor.constraintEqualToConstant(h).active = true

scrollview.layer.borderWidth = 2
scrollview.layer.borderColor = UIColor.greenColor().CGColor
scrollview.backgroundColor = UIColor.brownColor()

// Scroll view content
let contentView = UIView() //frame:CGRect(origin: CGPointZero, size:CGSize(width: width, height: height)))
scrollview.addSubview(contentView)
contentView.backgroundColor = UIColor.redColor()
contentView.layer.borderWidth = 10
contentView.layer.borderColor = UIColor.blueColor().CGColor
contentView.translatesAutoresizingMaskIntoConstraints = false
contentView.centerYAnchor.constraintEqualToAnchor(scrollview.centerYAnchor).active = true
contentView.widthAnchor .constraintEqualToConstant(width).active = true
contentView.heightAnchor.constraintEqualToConstant(height).active = true

return scrollview
}


Unfortunately, I cannot scroll it horizontally, see screenshot as follows:

enter image description here

What am I missing?

Answer

Here is the solution of my issue. I provided a tool function that solve the issue as follow:

func setScrollViewConstraints(scrollView: UIScrollView, contentView:UIView, multiplier m:(width:CGFloat, height:CGFloat)=(1, 1)) {

    guard let scrollViewParent = scrollView.superview else {
        assert(false, "setScrollViewConstraints() requires the scrollview to have a superview")
        return
    }

    guard let contentViewParent = contentView.superview else {
        assert(false, "setScrollViewConstraints() requires the contentView to have a superview")
        return
    }

    guard contentViewParent == scrollView else {
        assert(false, "setScrollViewConstraints() requires the contentView to have a superview being the scrollView")
        return
    }

    scrollView.translatesAutoresizingMaskIntoConstraints = false
    scrollView.widthAnchor  .constraintEqualToAnchor(scrollViewParent.widthAnchor,  multiplier: m.width ).active = true
    scrollView.heightAnchor .constraintEqualToAnchor(scrollViewParent.heightAnchor, multiplier: m.height).active = true


    contentView.translatesAutoresizingMaskIntoConstraints = false
    contentView.leftAnchor  .constraintEqualToAnchor(contentViewParent.leftAnchor      ).active = true
    contentView.rightAnchor .constraintEqualToAnchor(contentViewParent.rightAnchor     ).active = true
    contentView.topAnchor   .constraintEqualToAnchor(contentViewParent.topAnchor       ).active = true
    contentView.bottomAnchor.constraintEqualToAnchor(contentViewParent.bottomAnchor    ).active = true

   /*

 // Pre-iOS 9 version

 let views = [
 "scrollView":scrollview,
 "contentView":contentView
 ]

 parent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[scrollView]|", options:[], metrics: [:], views:views))
 parent.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[scrollView]|", options:[], metrics: [:], views:views))

 scrollview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[contentView]|", options:[], metrics: [:], views:views))
 scrollview.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[contentView]|", options:[], metrics: [:], views:views))
 */

}

Use it like this for having the scroll view 100% width the super view and 1/4th of its height:

func addStripCategoryTo(parent: UIView) -> UIView {

    // Scroll view
    let scrollview = UIScrollView()
    parent.addSubview(scrollview)

    scrollview.backgroundColor    = UIColor.brownColor()
    scrollview.layer.borderWidth  = 2
    scrollview.layer.borderColor  = UIColor.greenColor().CGColor

    // Scroll view content
    let contentView = UIImageView()
    contentView.image = UIImage(named: "cover-0.jpeg")
    scrollview.addSubview(contentView)

    contentView.backgroundColor   = UIColor.redColor()
    contentView.layer.borderWidth = 10
    contentView.layer.borderColor = UIColor.blueColor().CGColor

    // Apply constraints
    setScrollViewConstraints(scrollview, contentView:contentView, multiplier: (1, 0.25))

    return scrollview
}