Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 414
Andrew Avatar asked Feb 26 '21 18:02

Andrew


Video Answer


2 Answers

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])
like image 200
Palli Avatar answered Sep 21 '22 02:09

Palli


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.

like image 26
dariowskii Avatar answered Sep 18 '22 02:09

dariowskii