I'm using GeometryReader() and .frame() to show their comparability and playing with their width. However the object wrapped in GeometryReader() doesn't have center alignment when width is less than minimum. Please refer to attached gifs for demo.
Could you please help me with alignment?
Ideal:
struct ExampleView: View {
@State private var width: CGFloat = 50
var body: some View {
VStack {
SubView()
.frame(width: self.width, height: 120)
.border(Color.blue, width: 2)
Text("Offered Width is \(Int(width))")
Slider(value: $width, in: 0...200, step: 5)
}
}
}
struct SubView: View {
var body: some View {
GeometryReader { geometry in
Rectangle()
.fill(Color.yellow.opacity(0.6))
.frame(width: max(geometry.size.width, 120), height: max(geometry.size.height, 120))
}
}
}
CodePudding user response:
That's because GeometryReader
doesn't center its children.
You have to manually position the Rectangle
by adding either a .position
modifier or a .offset
.
.position
will move the origin of the rectangle's frame relative to the parent's center.
.offset
will not change the frame, but rather change the rendering (working like a CGAffineTransform
with translation).
The following modifiers to your Rectangle
will yield the same results visually (though different under the hood):
Rectangle()
.fill(Color.yellow.opacity(0.6))
.frame(width: max(geometry.size.width, 120), height: max(geometry.size.height, 120))
.position(x: geometry.size.width / 2, y: geometry.size.height / 2)
or
let rectWidth = max(geometry.size.width, 120)
Rectangle()
.fill(Color.yellow.opacity(0.6))
.frame(width: rectWidth, height: max(geometry.size.height, 120))
.offset(x: ((geometry.size.width - rectWidth) / 2), y: 0)
Note that your rectangle's frame exceeds the bounds of its parent. I'd suggest to avoid that because it will cause all sorts of difficulties laying out other UI elements on the screen.
You could build it the other way around (unless your practical goal is to just understand how GeometryReader
works):
struct ExampleView: View {
@State private var width: CGFloat = 50
var body: some View {
VStack {
let minWidth: CGFloat = 120
let subViewWidth: CGFloat = max(minWidth, width)
SubView(desiredWidth: width)
.frame(width: subViewWidth, height: 120)
.background(.yellow.opacity(0.6))
Text("Offered Width is \(Int(width))")
Slider(value: $width, in: 0...200, step: 5)
}
}
}
struct SubView: View {
let desiredWidth: CGFloat
var body: some View {
Rectangle()
.fill(.clear)
.border(Color.blue, width: 2)
.frame(width: desiredWidth, height: nil)
}
}
In this example the SubView
has a yellow fill and a minimal frame width while the inner rectangle just takes whatever width is set on the slider. It also doesn't need a GeometryReader
anymore. It looks and behaves the same but none of the views exceeds its parent's bounds anymore.