How to create the equivalent of a SwiftUI stack view spacer programmatically
Asked Answered
P

4

12

I'm working on trying to recreate the first SwiftUI Tutorial in code without using SwiftUI. In the example, it mentions using a Spacer "to direct the layout to use the full width of the device":

VStack {
  Text("Turtle Rock")
  HStack {
    Text("Joshua Tree National Park")
    Spacer()
    Text("California")
  }
}

For the VStack, HStack, and Text views, I can easily use UIStackView and UILabel. But for the Spacer, I can't seem to find any equivalent in the standard library (no UISpacer or anything like that), which makes me think this is something custom to SwiftUI. In the tutorial, it describes how this Spacer works:

A spacer expands to make its containing view use all of the space of its parent view, instead of having its size defined only by its contents.

So how can I recreate the functionality of this Spacer view programmatically? Do I just add a constraint to the UIStackView to make it full width? Or is there a way to add a subview to the UIStackView that makes it behave like it does in SwiftUI?

Punak answered 26/2, 2021 at 18:43 Comment(2)
You can set stack view distribution = .equalSpacingSoloman
you can use UILayoutGuide as a container. Is this helpful -: developer.apple.com/documentation/uikit/uilayoutguideSchnapp
W
17

I have worked solution using dummy UIView() which has set big constant widthConstraint using low priority - this ensures UIView will grow as much as possible but will not grow over superview constraints since it has lower priority.

Example:

let titleLabel = UILabel()
titleLabel.text = "Joshua Tree National Park"

let spacer = UIView()
// maximum width constraint
let spacerWidthConstraint = spacer.widthAnchor.constraint(equalToConstant: .greatestFiniteMagnitude) // or some very high constant
spacerWidthConstraint.priority = .defaultLow // ensures it will not "overgrow"
spacerWidthConstraint.isActive = true

let descriptionLabel = UILabel()
descriptionLabel.text = "California"

UIStackView(arrangedSubviews: [titleLabel, spacer, descriptionLabel])
Weinman answered 19/8, 2021 at 8:49 Comment(1)
I haven’t tried this yet but it looks promising. Thanks for sharing!Punak
B
4

Swift 5

You can create an extension for UIView to create a spacer in this way:

extension UIView {

    static func spacer(size: CGFloat = 10, for layout: NSLayoutConstraint.Axis = .horizontal) -> UIView {
        let spacer = UIView()
        
        if layout == .horizontal {
            spacer.widthAnchor.constraint(equalToConstant: size).isActive = true
        } else {
            spacer.heightAnchor.constraint(equalToConstant: size).isActive = true
        }
        
        return spacer
    }

}

After that, you can add your spacer into stacks in this way:

// ... some code

let stack = UIStackView(arrangedSubviews: [UIView.spacer(), UILabel(), UIView.spacer(), UILabel(), UIView.spacer()])
stack.axis = .horizontal
stack.distribution = .equalSpacing

// For vertical stacks

let stack = UIStackView(arrangedSubviews: [UIView.spacer(for: .vertical), UILabel(), UIView.spacer(for: .vertical), UILabel(), UIView.spacer(for: .vertical)])
stack.axis = .vertical
stack.distribution = .equalSpacing

I think in this way, you can play with spacer size/layout and stack distribution/alignment to get the desired space.

Bracy answered 17/11, 2021 at 12:31 Comment(0)
W
3
import UIKit

extension UIView {
    public static var spacerView: UIView {
        let view = UIView()
        view.isUserInteractionEnabled = false
        view.setContentHuggingPriority(.fittingSizeLevel, for: .horizontal)
        view.setContentCompressionResistancePriority(.fittingSizeLevel, for: .horizontal)
        return view
    }
}

And use it like:

stackView.addArrangedSubview(Self.spacerView)

You will have your very own SwiftUI's Spacer() equivalent in UIKit.

Waterscape answered 19/6, 2023 at 21:27 Comment(0)
E
1

A plain UIView() can replace a SwiftUI.Spacer as long as distribution is set to fill, since it has no set width constraint. I've only tested this vertically. However, you should note the layout performed by UIStackView and SwiftUI.HStack are not actually the same steps, though the end results may appear similar; something to look into.

Encroachment answered 13/8, 2021 at 0:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.