skip to Main Content

I have a UIStackView in storyboard like this:

@IBOutlet weak var stackView: UIStackView!

I want to be able to add views to this UIStackView programmatically like this:

    let view = UIView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
    view.backgroundColor = .gray        
    stackView.addArrangedSubview(view)

Initially, the views added will be 50×50 in dimensions. When only a few are added, I want them to stay 50×50 and be centered in the screen.

But when you add more than there is width on the screen, I want them to shrink in size.

Here is what I want it to look like. The first row is with three 50×50 views added. The next row has more added and notice how they shrink in width to fit the width of the screen:

enter image description here

Is this possible with a UIStackView and adding views programmatically like this?

2

Answers


  1. Yes, It’s possible.
    You can set set the following thing:

    Constraints:

    1. Add some height constraint that you want.
    2. Add minimum width to stack view.
    3. Add X and Y position to stack view.

    enter image description here

    StackView Properties:

    1. Set alignment to fill.
    2. Set distribution to fill equally.
    3. Add some spacing, I’ve added 5.
      enter image description here

    Code:

    1. Take IBOutlet of stack view width in UIViewController class.
    2. Calculate width of stack view as:
    width = ({number of subviews} * {width of one subview}) + ({number of subviews - 1} * {stack view spacing added in step 3 of properties})
    
    1. Set this width to constraint of stack view when update or add remove on subviews.

    Now, add subviews programmatically.

    Login or Signup to reply.
  2. This can be done with auto layout constraints only… no need to "calculate" sizing.

    Start with a stack view:

    • a UIStackView
      • horizontal axis
      • spacing as desired
      • Distribution: Fill Equally
    • constrain the Height to 50
    • constrain centerX to view centerX
    • constrain Leading greaterThanOrEqualTo view leading plus 8-points ("side padding")
    • constrain Trailing lessThanOrEqualTo view trailing minus 8-points ("side padding")

    Every time you add an arranged subview, give it a Width constraint of 50 — but with a Priority of .defaultHigh.

    That tells auto layout: *"TRY to make the view 50-points wide. If you can’t, then use the next auto layout command for its width." In this case, the "next" command will be the stack view’s Fill Equally property.

    Here’s some quick example code. A stack view near the top, starting with one subview… along with "Add" and "Remove" buttons:

    class ViewController: UIViewController {
        
        let stackView = UIStackView()
        
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // stack view properties
            stackView.axis = .horizontal
            stackView.distribution = .fillEqually
            stackView.spacing = 6
    
            stackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(stackView)
            
            let g = view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                
                stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
                
                // we want height always at 50-points
                stackView.heightAnchor.constraint(equalToConstant: 50.0),
                
                // keep left and right ends of stack view AT LEAST 8-points from the sides
                stackView.leadingAnchor.constraint(greaterThanOrEqualTo: g.leadingAnchor, constant: 8.0),
                stackView.trailingAnchor.constraint(lessThanOrEqualTo: g.trailingAnchor, constant: -8.0),
    
                // centered horizontally
                stackView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
                
            ])
            
            // "Add" and "Remove" buttons
            var cfg = UIButton.Configuration.filled()
            
            cfg.title = "Add"
            
            let addBtn = UIButton(configuration: cfg, primaryAction: UIAction() { _ in
                self.addView()
            })
            
            cfg.title = "Remove"
            
            let remBtn = UIButton(configuration: cfg, primaryAction: UIAction() { _ in
                self.removeView()
            })
            
            addBtn.translatesAutoresizingMaskIntoConstraints = false
            remBtn.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(addBtn)
            view.addSubview(remBtn)
            
            NSLayoutConstraint.activate([
                
                addBtn.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 20.0),
                addBtn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
                
                remBtn.leadingAnchor.constraint(equalTo: addBtn.trailingAnchor, constant: 20.0),
    
                remBtn.topAnchor.constraint(equalTo: stackView.bottomAnchor, constant: 20.0),
                remBtn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
    
                remBtn.widthAnchor.constraint(equalTo: addBtn.widthAnchor),
                
            ])
            
            // start with one arranged subview
            addView()
            
        }
        
        func addView() {
            let v = UIView()
            v.backgroundColor = .lightGray
            // tell autolayout to TRY to make the arranged subview 50-points width
            //  but allow it to "break"
            let w: NSLayoutConstraint = v.widthAnchor.constraint(equalToConstant: 50.0)
            w.priority = .defaultHigh
            w.isActive = true
            stackView.addArrangedSubview(v)
        }
        
        func removeView() {
            // keep at least one arranged subview
            if stackView.arrangedSubviews.count > 1 {
                stackView.arrangedSubviews.last?.removeFromSuperview()
            }
        }
        
    }
    

    Looks like this:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search