tiltowait
tiltowait

Reputation: 110

Nested UIStackViews inside a UIScrollView: Not filling width of container?

I'm trying to programmatically create a 3x33 grid of UIButtons using nested UIStackViews inside a UIScrollView. The outer UIStackView has a vertical axis, while the inner UIStackViews have horizontal axes and distribution = .fillEqually.

If I don't have the enclosing UIScrollView, the buttons properly fill the width of the screen. With the scroll view, however, the buttons fill only half the width of the screen.

Here is a code example. In IB, ViewController's view property has been set to a UIScrollView instance.

class ViewController: UIViewController {
  var stackView: UIStackView!

  override func viewDidLoad() {
    super.viewDidLoad()

    title = "No Filling :("

    stackView = UIStackView()
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.axis = .vertical
    stackView.spacing = 5
    view.addSubview(stackView)

    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[stackView]-|", options: .alignAllCenterX, metrics: nil, views: ["stackView": stackView!]))
    view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[stackView]|", options: .alignAllCenterY, metrics: nil, views: ["stackView": stackView!]))

    for _ in 1...33 {
      let hView = UIStackView()
      hView.translatesAutoresizingMaskIntoConstraints = false
      hView.axis = .horizontal
      hView.distribution = .fillEqually
      hView.spacing = 5

      stackView.addArrangedSubview(hView)
      stackView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[hView]|", options: .alignAllCenterX, metrics: nil, views: ["hView": hView]))

      for i in 1...3 {
        let button = UIButton(type: .system)
        button.setTitle("Button \(i)", for: .normal)
        hView.addArrangedSubview(button)

        button.layer.borderWidth = 1
        button.layer.cornerRadius = 7
      }
    }
  }

}

Upvotes: 0

Views: 269

Answers (1)

vacawama
vacawama

Reputation: 154563

You want your scrollView's content width to be the width of the scrollView itself so that it fills the screen and scrolls vertically.

In order to do that, you need to establish the width of the scrollView's content. Right now, it is getting its width from the intrinsic size of the three buttons.

You need to add a constraint that explicitly makes the scrollView's content (the stackView + left and right offsets) equal to the width of the scrollView:

view.addConstraints(NSLayoutConstraint.constraints(
    withVisualFormat: "H:|-20-[stackView]-20-|", options: .alignAllCenterX,
    metrics: nil, views: ["stackView": stackView!]))

view.addConstraints(NSLayoutConstraint.constraints(
    withVisualFormat: "V:|[stackView]|", options: .alignAllCenterY,
    metrics: nil, views: ["stackView": stackView!]))

stackView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -40).isActive = true

Note: The adjustment constant -40 is the sum of the left and right offsets (20 each). In order for the scrollView to not scroll horizontally: stackView.width + 20 + 20 == scrollView.width, or equivalently, stackView.width == scrollView.width - 40 which is what the constraint specifies.

Upvotes: 1

Related Questions