Hello, I came across a strange behaviour of GeometryReader in a NavigationStack. I want to get the size of an image view on the screen, so I add a .background modifier with a GeometryReader inside
.background( GeometryReader { geo in
Color.clear
.onAppear {
imgW = geo.size.width
imgH = geo.size.height
}
When I use this on a pure image view it works fine.
When this image is inside a NavigationStack I get wrong results when the image is shown the first time. But I get the right results when I move to the next view and come back to this view. Now GeometryReader gives the right size.
Is it a bug or do I miss something?
For demonstration I wrote a short program with 3 views:
import SwiftUI
class Router: ObservableObject {
@Published var path = NavigationPath()
}
enum Routes: String, CaseIterable, Hashable {
case View2, View3
}
struct ContentView: View {
@StateObject var router = Router()
@State private var imgSize = CGSize()
var body: some View {
NavigationStack(path: $router.path) {
VStack {
Image(uiImage: UIImage(named: "testpicture")!)
.resizable()
.scaledToFit()
.background( GeometryReader { geo in
Color.clear
.onAppear {
imgSize = geo.size
}
})
Text("Width \(Int(imgSize.width)) - Height \(Int(imgSize.height))")
Button {
router.path.append(Routes.View2)
} label: {
Text("Press for next view")
}
}
.navigationDestination(for: Routes.self) { route in
switch route {
case .View2:
View2()
case .View3:
View3()
}
}
}
.environmentObject(router)
}
}
struct View2: View {
@EnvironmentObject var routes: Router
@State private var imgSize = CGSize()
var body: some View {
VStack {
Image(uiImage: UIImage(named: "testpicture")!)
.resizable()
.scaledToFit()
.background( GeometryReader { geo in
Color.clear
.onAppear {
imgSize = geo.size
}
})
Text("Width \(Int(imgSize.width)) - Height \(Int(imgSize.height))")
Button {
routes.path.append(Routes.View3)
} label: {
Text("Press for next view")
}
}
.navigationTitle("View 2")
.navigationBarTitleDisplayMode(.inline)
}
}
struct View3: View {
@EnvironmentObject var routes: Router
var body: some View {
VStack {
Text("Hello World")
Text("& Friends")
}
.navigationTitle("View 3")
}
}
"testpicture" is a normal photo in the assets section.
Test in landscape mode (iPhone 15 plus simulator):
- Startview shows the picture and "Width 0 - Height 0" (WRONG)
- Press the button to show the next view
- View2 shows the picture and "Width 285 - Height 380" (WRONG)
- Going back to the startview shows "Width 269 - Height 359" (CORRECT)
- Switching to View2 still gives the wrong results (285, 380)
- Going to View3 and back to View2 gives the CORRECT result ("Width 236 - Height 315")
Same behaviour on a real device.
Side note: Going to View3 and back again to View2 gives on every transfer a warning: "Failed to create 0x132 image slot (alpha=1 wide=1) (client=0xe03620e7) [0x5 (os/kern) failure]"
As usual: after I searched for a topic without success, I post in a forum, search again .... and find the solution. On stackoverflow there is a post describing this problem and the answer for NavigationView https://stackoverflow.com/questions/67327453/geometryreader-with-navigationview-in-swiftui-is-initially-giving-zero-for-size).
The solution is to use ".onChange(of: geo.size)".