LinusG.
LinusG.

Reputation: 28892

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:

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

Upvotes: 5

Views: 6292

Answers (3)

Jay Whitsitt
Jay Whitsitt

Reputation: 1045

I also noticed that custom spacing values get reset after hiding/unhiding children. I was able to override updateConstraints() for my parent view and set the custom spacing as needed. The views then kept their intended spacing.

override func updateConstraints() {
  super.updateConstraints()
  stackView.setCustomSpacing(10, after: childView)
}

Upvotes: 0

Eric
Eric

Reputation: 16921

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)

Upvotes: 9

Fabio Felici
Fabio Felici

Reputation: 2906

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

Upvotes: 18

Related Questions