1

I am trying to put two UIViews inside a horizontal UIStackview. Inside the UIViews are Vertical UIStackviews to space the text (See picture for example). The vertical stackviews in the UIView is working. When I try to add the two UIViews to the horizontal stackview the UIViews are getting overlapped and I cant seem to space them properly. How can I space it like the example. I can't see the problem in the constraints. I tried a lot of different solutions but I cant figure it out.

enter image description here

Declarations

private let horizontalStackview: UIStackView = {
        let horizontalStackview = UIStackView()
        horizontalStackview.distribution = .fillEqually
        horizontalStackview.axis = .horizontal
        horizontalStackview.spacing = 200
        uhorizontalStackview.translatesAutoresizingMaskIntoConstraints = false
        return horizontalStackview
    }()
    
    private let verticalFirstUIView: UIView = {
        let verticalFirstUIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
        verticalFirstUIView.backgroundColor = .white
        verticalFirstUIView.layer.borderWidth = 2
        verticalFirstUIView.layer.borderColor = UIColor.gray.cgColor
        verticalFirstUIView.translatesAutoresizingMaskIntoConstraints = false
        return verticalFirstUIView
    }()
    
    private let verticalSecondUIView: UIView = {
        let verticalSecondUIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
        verticalSecondUIView.backgroundColor = .white
        verticalSecondUIView.layer.borderWidth = 2
        verticalSecondUIViewlayer.borderColor = UIColor.blue.cgColor
        verticalSecondUIView.translatesAutoresizingMaskIntoConstraints = false
        return verticalSecondUIView
    }()
    
    
    private let verticalFirstStackView: UIStackView = {
        let verticalFirstStackView = UIStackView()
       verticalFirstStackView.axis = .vertical
       verticalFirstStackView.alignment = .center
        verticalFirstStackViewdistribution = .fillEqually
        verticalFirstStackViewspacing = 5
        verticalFirstStackView.translatesAutoresizingMaskIntoConstraints = false
        return verticalFirstStackView
    }()
    
    private let verticalSecondStackView: UIStackView = {
        let verticalSecondStackView = UIStackView()
       verticalSecondStackView.axis = .vertical
        verticalSecondStackView.alignment = .center
        verticalSecondStackViewdistribution = .fillEqually
        verticalSecondStackView.spacing = 5
        verticalSecondStackView.translatesAutoresizingMaskIntoConstraints = false
        return verticalSecondStackView
    }()

Functions with constraints

func setupHorizontalStackview(){
        view.addSubview(horizontalStackview)

        horizontalStackview.addArrangedSubview(verticalFirstUIView)
        horizontalStackview.addArrangedSubview(verticalSecondUIView)
        NSLayoutConstraint.activate([
           horizontalStackview.topAnchor.constraint(equalTo: labelAboveHorizontalStackView.bottomAnchor, constant: 10),
            horizontalStackview.widthAnchor.constraint(equalToConstant: horizontalStackview.frame.width)
        ])
    }
    

    func setupVerticalFirstUIView(){
        view.addSubview(verticalFirstUIView)
        userSavedUIView.addSubview(verticalFirstStackView)
        NSLayoutConstraint.activate([   verticalFirstUIView.topAnchor.constraint(equalTo: horizontalStackview.topAnchor),
            verticalFirstUIView.widthAnchor.constraint(equalToConstant: verticalFirstUIView.frame.width)
        ])
    }

    func setupVerticalSecondUIView(){
        view.addSubview(verticalSecondUIView)
       verticalSecondUIView.addSubview(verticalSecondStackView)
        NSLayoutConstraint.activate([
            verticalSecondUIView.topAnchor.constraint(equalTo: horizontalStackview.topAnchor),
            verticalSecondUIView.widthAnchor.constraint(equalToConstant: verticalSecondUIView.frame.width)
        ])
    }

    func setupverticalFirstStackView(){
        view.addSubview(verticalFirstStackView)
        NSLayoutConstraint.activate([
            verticalFirstStackView.leadingAnchor.constraint(equalTo: verticalFirstUIView.leadingAnchor, constant: 15),
           verticalFirstStackView.trailingAnchor.constraint(equalTo: verticalFirstUIView.trailingAnchor, constant: -15),
            verticalFirstStackView.topAnchor.constraint(equalTo: verticalFirstUIView.topAnchor, constant: 15),
            verticalFirstStackView.bottomAnchor.constraint(equalTo: verticalFirstUIView.bottomAnchor),
        ])

       let ulText = UILabel()
        let ulTextTwo = UILabel()
        let ulTextThree = UILabel()


    ulText.text = "text”
    ulTextTwo.text = "text2”
    ulTextThree.text = "text3”


        verticalFirstStackView.addArrangedSubview(ulText)
        verticalFirstStackView.addArrangedSubview(ulTextTwo)
        verticalFirstStackView.addArrangedSubview(ulTextThree)
    }



     func setupverticalSecondStackView(){
        view.addSubview(verticalSecondStackView)
        NSLayoutConstraint.activate([
            verticalSecondStackView.leadingAnchor.constraint(equalTo: verticalSecondUIView.leadingAnchor, constant: 15),
           verticalSecondStackView.trailingAnchor.constraint(equalTo: verticalSecondUIView.trailingAnchor, constant: -15),
            verticalSecondStackView.topAnchor.constraint(equalTo: verticalSecondUIView.topAnchor, constant: 15),
            verticalSecondStackView.bottomAnchor.constraint(equalTo: verticalSecondUIView.bottomAnchor),
        ])

       let ulText = UILabel()
        let ulTextTwo = UILabel()
        let ulTextThree = UILabel()


    ulText.text = "text”
    ulTextTwo.text = "text2”
    ulTextThree.text = "text3”


       verticalSecondStackView.addArrangedSubview(ulText)
        verticalSecondStackView.addArrangedSubview(ulTextTwo)
        verticalSecondStackView.addArrangedSubview(ulTextThree)
}

I appreciate all help.

Mashiba Sakuto
  • 213
  • 4
  • 15

1 Answers1

2

First, you have a very odd constraint line:

horizontalStackview.widthAnchor.constraint(equalToConstant: horizontalStackview.frame.width)

You're telling auto-layout to make the horizontal stackView width equal to its own width. That doesn't make any sense.

Second, while it can be valuable to split tasks into separate funcs, it can make it difficult to follow what's going on. I'd suggest starting with everything "in-line" -- along with comments about what the code should be doing -- and then break it up later if desired.

What we want to do is:

  • declare the "label above the horizontal stack view"
  • declare the horizontal stack view
  • declare the two vertical stack views
  • declare the two "UI" views (which hold the vertical stack views)

So far, that's pretty much what you've done.

Now, let's logically setup the elements:

  • add three labels to each of the vertical stack views
  • add those stack views to their respective "UI" views
  • constrain each stack view to its "UI" view
  • add the two "UI" views to the horizontal stack view

then:

  • add the label to the view
  • add the horizontal stack view to the view
  • add constraints for the label
  • add constraints for the horizontal stack view

Review the way it is done here:

class ViewController: UIViewController {
    
    // a label above the horizontal stack view
    private let labelAboveHorizontalStackView: UILabel = {
        let v = UILabel()
        v.text = "Label above stack view"
        v.textAlignment = .center
        v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    private let horizontalStackview: UIStackView = {
        let horizontalStackview = UIStackView()
        horizontalStackview.distribution = .fillEqually
        horizontalStackview.axis = .horizontal
        horizontalStackview.spacing = 200
        horizontalStackview.translatesAutoresizingMaskIntoConstraints = false
        return horizontalStackview
    }()
    
    private let verticalFirstUIView: UIView = {
        let verticalFirstUIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
        verticalFirstUIView.backgroundColor = .white
        verticalFirstUIView.layer.borderWidth = 2
        verticalFirstUIView.layer.borderColor = UIColor.gray.cgColor
        verticalFirstUIView.translatesAutoresizingMaskIntoConstraints = false
        return verticalFirstUIView
    }()
    
    private let verticalSecondUIView: UIView = {
        let verticalSecondUIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 80))
        verticalSecondUIView.backgroundColor = .white
        verticalSecondUIView.layer.borderWidth = 2
        verticalSecondUIView.layer.borderColor = UIColor.blue.cgColor
        verticalSecondUIView.translatesAutoresizingMaskIntoConstraints = false
        return verticalSecondUIView
    }()
    
    private let verticalFirstStackView: UIStackView = {
        let verticalFirstStackView = UIStackView()
        verticalFirstStackView.axis = .vertical
        verticalFirstStackView.alignment = .center
        verticalFirstStackView.distribution = .fillEqually
        verticalFirstStackView.spacing = 5
        verticalFirstStackView.translatesAutoresizingMaskIntoConstraints = false
        return verticalFirstStackView
    }()
    
    private let verticalSecondStackView: UIStackView = {
        let verticalSecondStackView = UIStackView()
        verticalSecondStackView.axis = .vertical
        verticalSecondStackView.alignment = .center
        verticalSecondStackView.distribution = .fillEqually
        verticalSecondStackView.spacing = 5
        verticalSecondStackView.translatesAutoresizingMaskIntoConstraints = false
        return verticalSecondStackView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // add 3 labels to each vertical stack view
        [verticalFirstStackView, verticalSecondStackView].forEach { sv in
            let ulText = UILabel()
            let ulTextTwo = UILabel()
            let ulTextThree = UILabel()

            ulText.text = "text"
            ulTextTwo.text = "text2"
            ulTextThree.text = "text3"

            // let's give the labels a background color to easily see the layout
            ulText.backgroundColor = .yellow
            ulTextTwo.backgroundColor = .green
            ulTextThree.backgroundColor = .cyan

            sv.addArrangedSubview(ulText)
            sv.addArrangedSubview(ulTextTwo)
            sv.addArrangedSubview(ulTextThree)
        }
        
        // add each vertical stack view as a subview of "UI" views
        verticalFirstUIView.addSubview(verticalFirstStackView)
        verticalSecondUIView.addSubview(verticalSecondStackView)
        
        // constrain the stack views to the "UI" views
        //  with 15-pts "padding" on Top / Leading / Trailing
        NSLayoutConstraint.activate([
            verticalFirstStackView.topAnchor.constraint(equalTo: verticalFirstUIView.topAnchor, constant: 15.0),
            verticalFirstStackView.leadingAnchor.constraint(equalTo: verticalFirstUIView.leadingAnchor, constant: 15.0),
            verticalFirstStackView.trailingAnchor.constraint(equalTo: verticalFirstUIView.trailingAnchor, constant: -15.0),
            verticalFirstStackView.bottomAnchor.constraint(equalTo: verticalFirstUIView.bottomAnchor, constant: 0.0),

            verticalSecondStackView.topAnchor.constraint(equalTo: verticalSecondUIView.topAnchor, constant: 15.0),
            verticalSecondStackView.leadingAnchor.constraint(equalTo: verticalSecondUIView.leadingAnchor, constant: 15.0),
            verticalSecondStackView.trailingAnchor.constraint(equalTo: verticalSecondUIView.trailingAnchor, constant: -15.0),
            verticalSecondStackView.bottomAnchor.constraint(equalTo: verticalSecondUIView.bottomAnchor, constant: 0.0),
        ])
        
        // add the two "UI" views to the horizontal stack view
        horizontalStackview.addArrangedSubview(verticalFirstUIView)
        horizontalStackview.addArrangedSubview(verticalSecondUIView)
        
        // add the "above label" to the view
        view.addSubview(labelAboveHorizontalStackView)
        
        // add horizontal stack view to the view
        view.addSubview(horizontalStackview)

        // let's respect the safe area
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            // label 40-pts from top
            labelAboveHorizontalStackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            // label centered horizontally
            labelAboveHorizontalStackView.centerXAnchor.constraint(equalTo: g.centerXAnchor),

            // stack view 10-pts below label
            horizontalStackview.topAnchor.constraint(equalTo: labelAboveHorizontalStackView.bottomAnchor, constant: 10.0),
            
            // allow the arranged subviews to determine the width?
            // if yes, center the horizontal stack view
            horizontalStackview.centerXAnchor.constraint(equalTo: g.centerXAnchor),

            // let's make the "above label" the same width as the
            //  resulting width of the horizontal stack view
            labelAboveHorizontalStackView.widthAnchor.constraint(equalTo: horizontalStackview.widthAnchor),
            
        ])
        
    }
    
}

and that gives us:

enter image description here

If that's not the exact result you want, I think you'll find it much easier to follow the structure and constraint setup by coding it this way.

Again, once you have the layout structure clarified and showing up as desired, you could then split sections of code into separate funcs (if you find a benefit to that).

DonMag
  • 69,424
  • 5
  • 50
  • 86
  • Thanks for the answer! It seems to display it much better now. Only thing I am struggling with is the vertical spacing between the three labels. I can't seem to reduce the spacing. Also the third label always has spacing at the bottom even though I set the bottom constraint to 0 and tried to adjust the height from : let verticalFirstUIView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 80)) but that didn't work. – Mashiba Sakuto Apr 13 '21 at 19:29
  • @Kryoke - your code showed the two vertical stack views each with `.spacing = 5` ... if that's not what you want, then you need to describe your goal a bit more clearly. – DonMag Apr 13 '21 at 19:56
  • I wanted 5 spacing but it showed 2 spacing between text 1 and 2, and 5 spacing between text 2 and 3, something weird was happening but I think it's fixed now. – Mashiba Sakuto Apr 13 '21 at 21:20