Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIStackView setCustomSpacing at runtime

I have a horizontal UIStackView that, by default, looks as follows:

default stack view

The view with the heart is initially hidden and then shown at runtime. I would like to reduce the spacing between the heart view and the account name view.
The following code does the job, but only, when executed in viewDidLoad:

stackView.setCustomSpacing(8, after: heartView)

When changing the custom spacing later on, say on a button press, it doesn't have any effect. Now, the issue here is, that the custom spacing is lost, once the subviews inside the stack view change: when un-/hiding views from the stack view, the custom spacing is reset and cannot be modified.

Things, I've tried:

  • verified the spacing is set by printing stackView.customSpacing(after: heartView) (which properly returns 8)
  • unsuccessfully ran several reload functions:
    • stackView.layoutIfNeeded()
    • stackView.layoutSubviews()
    • view.layoutIfNeeded()
    • view.layoutSubviews()
    • viewDidLayoutSubviews()

How can I update the custom spacing of my stack view at runtime?

like image 704
LinusGeffarth Avatar asked Mar 15 '18 17:03

LinusGeffarth


People also ask

What is Uistackview?

A streamlined interface for laying out a collection of views in either a column or a row.

How do you add a space in a stack?

1) Adjust the external space at the top and bottom outside the stack you are currently editing. Pull the slider right and space above and below your stack will increase, and vice versa if the bar is moved to the left. 2) Adjust the internal space, the top and bottom areas inside the stack you are currently editing.


2 Answers

You need to make sure the UIStackView's distribution property is set to .fill or .fillProportionally.


I created the following swift playground and it looks like I am able to use setCustomSpacing at runtime with random values and see the effect of that.

import UIKit
import PlaygroundSupport

public class VC: UIViewController {

    let view1 = UIView()
    let view2 = UIView()
    let view3 = UIView()
    var stackView: UIStackView!

    public init() {
        super.init(nibName: nil, bundle: nil)
    }

    public required init?(coder aDecoder: NSCoder) {
        fatalError()
    }

    public override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view1.backgroundColor = .red
        view2.backgroundColor = .green
        view3.backgroundColor = .blue

        view2.isHidden = true

        stackView = UIStackView(arrangedSubviews: [view1, view2, view3])
        stackView.spacing = 10
        stackView.axis = .horizontal
        stackView.distribution = .fillProportionally

        let uiSwitch = UISwitch()
        uiSwitch.addTarget(self, action: #selector(onSwitch), for: .valueChanged)

        view1.addSubview(uiSwitch)
        uiSwitch.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            uiSwitch.centerXAnchor.constraint(equalTo: view1.centerXAnchor),
            uiSwitch.centerYAnchor.constraint(equalTo: view1.centerYAnchor)
        ])

        view.addSubview(stackView)
        stackView.translatesAutoresizingMaskIntoConstraints = false

        NSLayoutConstraint.activate([
            stackView.heightAnchor.constraint(equalToConstant: 50),
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 50),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -50)
        ])
    }

    @objc public func onSwitch(sender: Any) {
        view2.isHidden = !view2.isHidden
        if !view2.isHidden {
            stackView.setCustomSpacing(CGFloat(arc4random_uniform(40)), after: view2)
        }
    }
}

PlaygroundPage.current.liveView = VC()
PlaygroundPage.current.needsIndefiniteExecution = true
like image 77
Fabio Felici Avatar answered Oct 10 '22 16:10

Fabio Felici


Another reason setCustomSpacing can fail is if you call it before adding the arranged subview after which you want to apply the spacing.

Won't work:

headerStackView.setCustomSpacing(50, after: myLabel)
headerStackView.addArrangedSubview(myLabel)

Will work:

headerStackView.addArrangedSubview(myLabel)
headerStackView.setCustomSpacing(50, after: myLabel)
like image 7
Eric Avatar answered Oct 10 '22 16:10

Eric