简体   繁体   中英

Swift how to put vertical UIStackview / UIView inside horizontal UIStackview programatically

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.

在此处输入图像描述

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.

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:

在此处输入图像描述

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM