Vladislav
Vladislav

Reputation: 217

How to layout 2 StackView of different sizes in a ScrollView

I am trying to fit 2 stackView in one ScrollView

I decided to create a generic StackView that will contain the other two StackViews and place it in a ScrollView

But my left StackView contains 1 item and the second contains 2 items

Code bellow:

        let categoryViewsForLeftColumn = categoryViews.split()[0]
        let categoryViewsForRightColumn = categoryViews.split()[1]
        
        let leftColumnStackView = UIStackView(arrangedSubviews: categoryViewsForLeftColumn)
        leftColumnStackView.translatesAutoresizingMaskIntoConstraints = false
        leftColumnStackView.axis = .vertical
        leftColumnStackView.spacing = 20
        leftColumnStackView.distribution = .fillEqually
        
        let rightColumnStackView = UIStackView(arrangedSubviews: categoryViewsForRightColumn)
        rightColumnStackView.translatesAutoresizingMaskIntoConstraints = false
        rightColumnStackView.axis = .vertical
        rightColumnStackView.spacing = 20
        rightColumnStackView.distribution = .fillEqually
        
        let generalStackView = UIStackView(arrangedSubviews: [leftColumnStackView, rightColumnStackView])
        generalStackView.translatesAutoresizingMaskIntoConstraints = false
        generalStackView.axis = .horizontal
        generalStackView.spacing = 20
        generalStackView.distribution = .fillEqually
        
        categoriesScrollView.addSubview(generalStackView)
        
        leftColumnStackView.backgroundColor = .green
        rightColumnStackView.backgroundColor = .red
        
        NSLayoutConstraint.activate([
            generalStackView.widthAnchor.constraint(equalTo: categoriesScrollView.widthAnchor, constant: -10),
            generalStackView.leadingAnchor.constraint(equalTo: categoriesScrollView.leadingAnchor, constant: 5),
            generalStackView.trailingAnchor.constraint(equalTo: categoriesScrollView.trailingAnchor, constant: -5),
            generalStackView.topAnchor.constraint(equalTo: categoriesScrollView.topAnchor, constant: 5),
            generalStackView.bottomAnchor.constraint(equalTo: categoriesScrollView.bottomAnchor, constant: -5)
        ])

result bellow:

result

I expect items to be the same size

I was trying to post 2 StackView to ScrollView

code bellow:

        categoriesScrollView.addSubview(leftColumnStackView)
        categoriesScrollView.addSubview(rightColumnStackView)
        
        leftColumnStackView.backgroundColor = .green
        rightColumnStackView.backgroundColor = .red
        
        NSLayoutConstraint.activate([
            leftColumnStackView.leadingAnchor.constraint(equalTo: categoriesScrollView.leadingAnchor, constant: 5),
            leftColumnStackView.trailingAnchor.constraint(equalTo: categoriesScrollView.centerXAnchor, constant: -5),
            leftColumnStackView.topAnchor.constraint(equalTo: categoriesScrollView.topAnchor, constant: 5),
            leftColumnStackView.bottomAnchor.constraint(equalTo: categoriesScrollView.bottomAnchor, constant: -5),
            
            rightColumnStackView.leadingAnchor.constraint(equalTo: categoriesScrollView.centerXAnchor, constant: 5),
            rightColumnStackView.trailingAnchor.constraint(equalTo: categoriesScrollView.trailingAnchor, constant: -5),
            rightColumnStackView.topAnchor.constraint(equalTo: categoriesScrollView.topAnchor, constant: 5),
            rightColumnStackView.bottomAnchor.constraint(equalTo: categoriesScrollView.bottomAnchor, constant: -5)
        ])

But it only got worse

result bellow:

second result

The result I want to achieve:

current result

Does anyone know how to achieve this?

Upvotes: 0

Views: 92

Answers (1)

DonMag
DonMag

Reputation: 77423

If you want to create a "grid" like that without using a collection view (and there are many good reasons not to), you can do it with stack views.

However, instead of think in terms of a horizontal stack view with two vertical "column" stack views, think about a vertical stack view with "rows" of horizontal stack views.

Here's a quick example...

First, a custom view with rounded sides, an image view and a label:

class PillView: UIView {
    
    let imgView = UIImageView()
    let label = UILabel()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() {
        label.numberOfLines = 0
        imgView.translatesAutoresizingMaskIntoConstraints = false
        label.translatesAutoresizingMaskIntoConstraints = false
        addSubview(imgView)
        addSubview(label)
        NSLayoutConstraint.activate([
            imgView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16.0),
            imgView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 12.0),
            imgView.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor, constant: -12.0),
            imgView.centerYAnchor.constraint(equalTo: centerYAnchor),
            imgView.widthAnchor.constraint(equalToConstant: 36.0),
            imgView.heightAnchor.constraint(equalTo: imgView.widthAnchor),
            
            label.topAnchor.constraint(equalTo: topAnchor, constant: 8.0),
            label.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8.0),
            label.leadingAnchor.constraint(equalTo: imgView.trailingAnchor, constant: 8.0),
            label.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16.0),

        ])
        layer.borderColor = UIColor.lightGray.cgColor
        layer.borderWidth = 1
    }
    
    override var bounds: CGRect {
        get { return super.bounds }
        set(newBounds) {
            super.bounds = newBounds
            layer.cornerRadius = newBounds.size.height / 2.0
        }
    }

}

Now, a sample controller:

class SampleViewController: UIViewController {
    
    let numViews: Int = 5
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let outerVerticalStack = UIStackView()
        outerVerticalStack.axis = .vertical
        outerVerticalStack.spacing = 20
        outerVerticalStack.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(outerVerticalStack)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            outerVerticalStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            outerVerticalStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            outerVerticalStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
        ])
        
        var j: Int = 0
        while j < numViews {
            let rowStack = UIStackView()
            rowStack.axis = .horizontal
            rowStack.spacing = 20
            rowStack.distribution = .fillEqually
            
            let v = PillView()
            if j == 2 {
                v.label.text = "View \(j)\ntwo lines"
            } else {
                v.label.text = "View \(j)"
            }
            if let img = UIImage(systemName: "\(j).square.fill") {
                v.imgView.image = img
            }
            rowStack.addArrangedSubview(v)
            
            j += 1
            
            if j < numViews {
                let v = PillView()
                v.label.text = "View \(j)"
                if let img = UIImage(systemName: "\(j).square.fill") {
                    v.imgView.image = img
                }
                rowStack.addArrangedSubview(v)
            } else {
                let v = UIView()
                rowStack.addArrangedSubview(v)
            }
            
            j += 1
            
            outerVerticalStack.addArrangedSubview(rowStack)
        }
        
    }
    
}

The result looks like this:

enter image description here

Upvotes: 1

Related Questions