user599807
user599807

Reputation:

Swift auto layout programmatically and dynamic

I have a View where I load buttons dynamically. So I have a for loop to loop through all buttons. Since this is dynamically I want to create the auto layout programmatically. Right now I have the following code:

for var i = 0; i < data.count; i++ {
      let button   = UIButton.buttonWithType(UIButtonType.System) as! UIButton
      button.backgroundColor = UIColor.greenColor()
      button.setTitle("Button", forState: UIControlState.Normal)

      button.setTranslatesAutoresizingMaskIntoConstraints(false)

      self.view.addSubview(button)

      let centerXConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0)

      let centerYConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant:15)

      let widthConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 200)

      let heightConstraint = NSLayoutConstraint(item: button, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant:100)

      scrollView.addConstraints([centerXConstraint, centerYConstraint, widthConstraint, heightConstraint])
}

This creates the first button and places it 15px under the top bar. The problem I have is how to place the next button 15px under the first one, the third button 15px under the second one etc. Anyone got any ideas?

Upvotes: 2

Views: 5178

Answers (1)

ABakerSmith
ABakerSmith

Reputation: 22939

Thats definitely possible, but firstly, I should mention it's not required that you add constraints for the buttons' width and height since they have an intrinsic content size (like UILabel) which depends on attributes such their text and font.

Back to your problem!

Here's the code, the explanation for each step is below:

override func viewDidLoad() {
    super.viewDidLoad()

    // 1.
    var upperView: UIView = scrollView

    for i in 0..<data.count {
        let button = UIButton.buttonWithType(UIButtonType.System) as! UIButton
        button.backgroundColor = UIColor.greenColor()
        button.setTitle("Button", forState: UIControlState.Normal)

        button.setTranslatesAutoresizingMaskIntoConstraints(false)

        //  2.
        scrollView.addSubview(button)

        //  3.
        let attribute: NSLayoutAttribute = i == 0 ? .Top : .Bottom

        //  4.
        let topEdgeConstraint = NSLayoutConstraint(item: button,
            attribute: .Top,
            relatedBy: .Equal,
            toItem: upperView,
            attribute: attribute,
            multiplier: 1.0,
            constant: 15.0)

        let centerXConstraint = NSLayoutConstraint(item: button,
            attribute: .CenterX,
            relatedBy: .Equal,
            toItem: scrollView,
            attribute: .CenterX,
            multiplier: 1.0,
            constant: 0.0)

        scrollView.addConstraint(topEdgeConstraint)
        scrollView.addConstraint(centerXConstraint)

        //  5.
        if i == data.count - 1 {
            let bottomConstraint = NSLayoutConstraint(item: button,
                attribute: .Bottom,
                relatedBy: .Equal,
                toItem: scrollView,
                attribute: .Bottom,
                multiplier: 1.0,
                constant: -15.0)

            scrollView.addConstraint(bottomConstraint)
        }

        upperView = button
    }
}

1. upperView is used to keep track of the view 'above' the current button. For example, when the first button is created, the upperView is the UIScrollView For the second button, upperView is the first button; for the third button, upperView is the second button and so on...

2. Your buttons should be added to the UIScrollView, not the self.view. Otherwise you'll get the error:

The view hierarchy is not prepared for the constraint...

3. This line selects the attribute on the upperView that will relate the button to the upperView. Here's a picture to demonstrate what I mean:

enter image description here

  • a) The .Top of the button is related to the .Top of the UIScrollView.

  • b) The .Top of the button is related to the .Bottom of the previous UIButton.

4. Making the top and centre X constraints - that's all pretty self explanatory.

5. For the UIScrollView to correctly calculate its contentSize it must have constraints in an unbroken chain from it top to bottom (the top to the bottom, in this case, because it needs to scroll vertically). Therefore, if it's the last UIButton a constraint from its bottom edge is added to the UIScrollView's bottom edge.

Hope that helps!

Upvotes: 15

Related Questions