Unfortunately, I don't think there's anything you can do to make NavigationView provide the correct geometry when initially displayed.
But if you do want access to the final geometry.size
from within your view, you can use onChange(of:)
as New Dev suggested:
struct ContentView: View {
@State var currentSize: CGSize?
var body: some View {
NavigationView {
GeometryReader { geometry in
Text("currentSize will soon be correct")
.onChange(of: geometry.size) { newSize in
currentSize = newSize
print(currentSize!) // prints (320.0, 457.0)
}
}
}
}
}
The above will work fine for many cases, but note that any local variables computed from geometry.size
within the GeometryReader's subviews will not be accurate in the onChange
block (it will capture the original, wrong value):
struct ContentView: View {
@State var currentSize: CGSize?
@State var halfWidth: CGFloat?
var body: some View {
NavigationView {
GeometryReader { geometry in
let halfWidthLocal = geometry.size.width / 2
Text("Half Width is really: \(halfWidthLocal)") // will read as "Half Width is really 160.000000"
.onChange(of: geometry.size) { newSize in
currentSize = newSize
halfWidth = halfWidthLocal
print(currentSize!) // prints (320.0, 457.0)
print(halfWidth!) // prints 0.0
}
}
}
}
}
In order to update state properties using the most up-to-date version of local variables, you can instead update the properties within a function that returns a view in your GeometryReader:
struct ContentView: View {
@State var currentSize: CGSize?
@State var halfWidth: CGFloat?
var body: some View {
NavigationView {
GeometryReader { geometry in
let halfWidthLocal = geometry.size.width / 2
makeText(halfWidthLocal: halfWidthLocal)
.onChange(of: geometry.size) { newSize in
currentSize = newSize
print(currentSize!) // prints (320.0, 457.0)
}
}
}
}
func makeText(halfWidthLocal: CGFloat) -> some View {
DispatchQueue.main.async { // Must update state properties on the main queue
halfWidth = halfWidthLocal
print(halfWidth!) // prints 0.0 the first time, then 160.0 the second time
}
return Text("Half Width is really: \(halfWidthLocal)") // will read as "Half Width is really 160.000000"
}
}
This type of situation came up for me, so just thought I'd pass on the knowledge to others.
.onChange(of: geometry.size) { ... }
it would work – ConfessorviewDidLoad
... usually, the frames are never right on the first load. Just like you would modify the frames inviewDidLayoutSubviews
, you would modify them inside.onChange
as @NewDev suggested – Cogon