How do you align content to the leading and trailing edges of another view in a SwiftUI VStack
?
I currently have the following SwiftUI code:
struct MyBlock: View {
var body: some View {
Rectangle().foregroundColor(.gray).frame(width: 80, height: 80)
}
}
struct MyGridView: View {
var body: some View {
HStack(spacing: 16) {
MyBlock()
MyBlock()
MyBlock()
}
}
}
struct PinPad: View {
var body: some View {
VStack {
MyGridView()
HStack {
Button(action: {}, label: { Text("Left Align") })
Spacer()
Button(action: {}, label: { Text("Right Align") })
}
}
}
}
The result renders like this:
But what I really want is for the "Left Align"
Button
to align with the left/leading edge of MyGridView
and the "Right Align"
Button
to align to the right/trailing edge of the MyGridView
.
Eg, like this:
I'm obviously missing something very basic here as doing this with Auto-Layout in UIKit is trivial.
SwiftUI Stack Alignment and Alignment Guides 1 Container Alignment. The most basic of alignment options when working with SwiftUI stacks is container alignment. ... 2 Alignment Guides. ... 3 Using the Alignment Guides Tool. ... 4 Custom Alignment Types. ... 5 Cross Stack Alignment. ... 6 ZStack Custom Alignment. ... 7 Summary. ...
Inevitably, when it comes to designing complex user interface layouts, it will be necessary to move beyond the standard alignment options provided with SwiftUI stack views.
Alignment guides are applied to views using the alignmentGuide () modifier which takes as arguments a standard alignment type and a closure which must calculate and return a value indicating the point within the view on which the alignment is to be based.
Views that do not have their own alignment guide are said to be implicitly aligned. When working with alignments it is important to remember that horizontal stacks (HStack) align child views vertically, while vertical stacks (VStack) align their children horizontally.
The first thing you need to do to troubleshoot layout issues, is to add borders on the views. This will let you see the real (and invisible) bounds of your views. The problem will become more clear:
In your case it can be easily fixed by wrapping it all inside a HStack and add some spacers:
struct PinPad: View {
var body: some View {
HStack {
Spacer()
VStack {
MyGridView().border(Color.blue, width: 3)
HStack {
Button(action: {}, label: { Text("Left Align") }).border(Color.red, width: 3)
Spacer()
Button(action: {}, label: { Text("Right Align") }).border(Color.red, width: 3)
}
}.border(Color.green, width: 3)
Spacer()
}
}
}
GeometryReader
is a view that gives you access to the size and position of your parent
Defined a view called GeometryGetter,
struct GeometryGetter: View {
@Binding var rect: CGRect
var body: some View {
return GeometryReader { geometry in
self.makeView(geometry: geometry)
}
}
func makeView(geometry: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.rect = geometry.frame(in: .global)
}
return Rectangle().fill(Color.clear)
}
}
Then change your code as below,
struct PinPad: View {
@State private var rect: CGRect = CGRect()
var body: some View {
HStack {
VStack {
MyGridView().background(GeometryGetter(rect: $rect))
HStack {
Button(action: {}, label: { Text("Left Align") })
Spacer()
Button(action: {}, label: { Text("Right Align") })
}.frame(width: rect.width)
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With