Search code examples
swiftuikituistackview

How to set space in stackview, like Spacer in SwiftUI?


How can I give spacing like Spacer in SwiftUI in stackview, not exact spacing?

private lazy var stackView: UIStackView = {
    let stackView = UIStackView()
    stackView.translatesAutoresizingMaskIntoConstraints = false
    stackView.distribution = .equalCentering
    stackView.axis = .horizontal
    //stackView.spacing = 100
    return stackView
}()

override func viewDidLoad() {
    super.viewDidLoad()
    setUI()
    view.backgroundColor = .white
}

func setUI() {
    view.addSubview(stackView)
    
    stackView.addArrangedSubview(usersTextLabel)
    stackView.addArrangedSubview(seeMoreButton)
    //stackView.addArrangedSubview(emptyView)
    
    stackView.snp.makeConstraints { make in
        make.top.equalToSuperview().inset(45)
        make.leading.equalToSuperview().inset(15)
        make.leading.equalToSuperview().inset(15)
    }
}

I tried to add empty view and give it some width. Is there another way to do it?

enter image description here


Solution

  • SwiftUI's layout system is very different from the constraint system of AutoLayout, so oftentimes there is no "direct translations".

    A Spacer, according to the documentation, is

    an adaptive view with no content that expands as much as it can. For example, when placed within an HStack, a spacer expands horizontally as much as the stack allows, moving sibling views out of the way, within the limits of the stack’s size.

    So we need to create a UIView that acts like that. Since it should expand as much as it can, the distribution of the stack view should be .fill (which is the default), not .equalCentering:

    stackView.distribution = .fill
    

    Note that "as much as it can" implies that the stack view should have a defined/maximum width. Otherwise it could expand indefinitely. Your stack view currently lacks this. You only constrained its top and left edges.

    From what I observed, in SwiftUI, HStacks can expand up to the bounds of its parent, so in this case you should constraint the trailing edge of your UIStackView too, defining its width.

    // similar to .padding(15)
    make.trailing.equalToSuperview().inset(-15)
    

    Then, assuming the other arranged views have an intrinsic/fixed width, you can just add a plain old UIView as an arranged subview of the stack view:

    let v = UIView()
    stackView.addArrangedSubview(v)
    

    The view will automatically expand to fill the space.

    If you want multiple spacers, you will need to specify their relative widths. The SwiftUI behaviour seems to be that all spacers in the same stack have the same width. So if you want 2 spacers, you can add a equal width constraint:

    spacer1.widthAnchor.constraint(equalTo: spacer2.widthAnchor).isActive = true
    

    If you have more spacers, just add more constraints.