Shades
Shades

Reputation: 5616

Arranging buttons programmatically with constraints

I have an array of buttons that I am iterating through and adding the buttons onto the view. Each button should be adjacent to the previous button, so I'm setting the leading constraint to the previous button's trailing. But the buttons end up layered on top of each other with only the top one displayed.

for k in 0 ..< buttons.count {

    view.addSubview(buttons[k])

    if k > 0 {
        buttons[k].leadingAnchor.constraint(equalTo: buttons[k-1].trailingAnchor).isActive = true
    }
}

Edit:

I don't know if this is part of the problem, but here's how I'm creating the buttons. I set each to (0,0) because I don't know where they'll end up. I assume the constraint would reposition them as needed (first time use programmatic constraints).

let size = CGRect(x: 0, y: 0, width: buttonWidth, height: buttonHeight)
let button: UIButton = UIButton(frame: size)

Upvotes: 2

Views: 4982

Answers (2)

interchen
interchen

Reputation: 57

The key problem is you should use isActive to active constraint.
The following is example

var buttons: [UIButton] = []
for index in 0...5 {
    let button = UIButton(frame: .zero)
    button.setTitleColor(.black, for: .normal)
    button.setTitle("button \(index)", for: .normal)
    button.layer.borderColor = UIColor.gray.cgColor
    button.layer.borderWidth = 1.0
    button.translatesAutoresizingMaskIntoConstraints = false
    self.view.addSubview(button)
    buttons.append(button)
}

for index in 0...5 {
    let button = buttons[index]
    if index == 0 {
        button.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 8.0).isActive = true
        button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 20.0).isActive = true
    } else {
        let preButton = buttons[index - 1]
        button.leadingAnchor.constraint(equalTo: preButton.trailingAnchor, constant: 8.0).isActive = true
        button.topAnchor.constraint(equalTo: preButton.topAnchor, constant: 0.0).isActive = true
    }
}

Upvotes: 2

Lorenzo B
Lorenzo B

Reputation: 33428

Here a simple playground that works with a UIStackView. You can play a bit and accommodate for your goal.

UIStackViews are very flexible components if you want avoid creating constraints manually.

//: A UIKit based Playground for presenting user interface

import UIKit
import PlaygroundSupport

class MyViewController: UIViewController {

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white

        let buttons = createButtons()
        let stackView = createStackView(with: UILayoutConstraintAxis.vertical)

        buttons.forEach { button in
            stackView.addArrangedSubview(button)
        }

        view.addSubview(stackView)

        stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        self.view = view
    }

    func createStackView(with layout: UILayoutConstraintAxis) -> UIStackView {
        let stackView = UIStackView()
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = layout
        stackView.distribution = .equalSpacing
        stackView.spacing = 0
        return stackView
    }

    func createButtons() -> [UIButton] {
        var buttons = [UIButton]()
        for x in 0..<5 {
            let button = UIButton(type: .custom)
            button.backgroundColor = .red
            button.translatesAutoresizingMaskIntoConstraints = false
            button.widthAnchor.constraint(equalToConstant: 50).isActive = true
            button.heightAnchor.constraint(equalToConstant: 100).isActive = true
            button.setTitle("Title \(x)", for: .normal)
            buttons.append(button)
        }
        return buttons
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

Upvotes: 4

Related Questions