johnny92 johnny92 - 7 months ago 48
iOS Question

Programmatically creating layout, using stack view and constraints not working

I'm trying to create this layout programmatically in Swift.

https://codepen.io/anon/pen/NXRQbJ

The first rectangle is small. And the other 2 rectangles are the same size but bigger than the first rectangle.

Here is the entire View Controller. I am not using storyboard. All my code is in this view controller.

import UIKit

class ViewController: UIViewController {

lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [smallRectangleView, bigRectangleView, bigRectangleView2])
stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.axis = .vertical
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()

var smallRectangleView: UIView = {
let view = UIView()
view.backgroundColor = .orange
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

var bigRectangleView: UIView = {
let view = UIView()
view.backgroundColor = .green
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

var bigRectangleView2: UIView = {
let view = UIView()
view.backgroundColor = .purple
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setupLayout()
print(stackView.frame)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func setupLayout() {
// Stack View
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

// Small Recntangle View
let heightConstraint1 = NSLayoutConstraint(item: smallRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView, attribute: .height, multiplier: 4.0, constant: 0.0)

// Big Rectangle View
let heightConstraint2 = NSLayoutConstraint(item: bigRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView2, attribute: .height, multiplier: 0.0, constant: 0.0)

view.addConstraints([heightConstraint1, heightConstraint2])
}
}


heightConstraint1 and heightConstraint2 is causing errors. I don't know why.

// Small Recntangle View
let heightConstraint1 = NSLayoutConstraint(item: smallRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView, attribute: .height, multiplier: 4.0, constant: 0.0)

// Big Rectangle View
let heightConstraint2 = NSLayoutConstraint(item: bigRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView2, attribute: .height, multiplier: 0.0, constant: 0.0)

view.addConstraints([heightConstraint1, heightConstraint2])


When I run this app on the simulator nothing shows up. It's just a blank white screen. Also in the debugger there are issues with constraints.

Answer Source

The main problem seems to be the stackView.distribution. I changed it from .fillProportionally to just .fill. I suspect the issue was that the views were already relative to each other because of your constraints and that lead to conflicts.

Other changes I made, your multiplier in one of your constraints needs to be 0.25 instead of 4, and in the second one the multiplier needs to be 1 instead of 0.

I also used NSLayoutConstraint.activate to activate the last 2 constraints instead of adding them to the view. In general, you want to activate constraints instead of adding them to views. Let iOS figure out which views to add them to.

class ViewController: UIViewController {

    lazy var stackView: UIStackView = {
        let stackView = UIStackView(arrangedSubviews: [smallRectangleView, bigRectangleView, bigRectangleView2])
        stackView.alignment = .fill
        stackView.distribution = .fill
        stackView.axis = .vertical
        stackView.translatesAutoresizingMaskIntoConstraints = false
        return stackView
    }()

    var smallRectangleView: UIView = {
        let view = UIView()
        view.backgroundColor = .orange
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    var bigRectangleView: UIView = {
        let view = UIView()
        view.backgroundColor = .green
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    var bigRectangleView2: UIView = {
        let view = UIView()
        view.backgroundColor = .purple
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        setupLayout()
        print(stackView.frame)
    }

    func setupLayout() {
        // Stack View
        view.addSubview(stackView)
        stackView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true

        // Small Recntangle View
        let heightConstraint1 = NSLayoutConstraint(item: smallRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView, attribute: .height, multiplier: 0.25, constant: 0.0)

        // Big Rectangle View
        let heightConstraint2 = NSLayoutConstraint(item: bigRectangleView, attribute: .height, relatedBy: .equal, toItem: bigRectangleView2, attribute: .height, multiplier: 1.0, constant: 0.0)

        NSLayoutConstraint.activate([heightConstraint1, heightConstraint2])
    }
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download