I think this may be related to a problem I've been having. A GeometryReader inside a .background or .overlay never runs/updates its children on a real device or simulator, but does work correctly in previews. I tried your suggestion of adding an AnyView and that made it work on the simulator and real phone. FWIW I made a simple demo of the bug. Without the AnyView(), on a real device or simulator the first Text stays stuck on "Square size unknown", but the second does update to "Did run updater: true".
Usually one should use .onAppear and/or .onChange(of:) to alter state, but they do not work in this context regardless of whether I use the AnyView hack. I've tried them in the GeometryUpdater, outside and inside the AnyView, and also in UpdateStateDuringViewUpdate. They never get called. This bug affects previews too.
import SwiftUI
struct UpdateStateDuringViewUpdate<T>: View {
init(_ stateBinding: Binding<T>, _ newValue: T) {
DispatchQueue.main.async {
stateBinding.wrappedValue = newValue
}
}
var body: some View { EmptyView() }
}
struct TestGeomReader: View {
@State var squareSize = "unknown"
@State var didRunUpdater = "false"
var body: some View {
VStack(alignment: .center) {
GreySquare()
.background {
GeometryUpdater(updatingStringWithSize: $squareSize)
}
Text("Square size \(squareSize)")
Text("Did run updater: \(didRunUpdater)")
}
}
@ViewBuilder func GreySquare() -> some View {
Rectangle()
.fill(Color(fromARGB: 0x80808080))
.frame(maxWidth: 200, maxHeight: 200, alignment: .center)
}
@ViewBuilder func GeometryUpdater(
updatingStringWithSize binding: Binding<String>
) -> some View {
UpdateStateDuringViewUpdate($didRunUpdater, "true")
GeometryReader { geom in
AnyView(UpdateStateDuringViewUpdate($squareSize, "\(geom.size.width)"))
}
}
}
struct TestGeomReader_Previews: PreviewProvider {
static var previews: some View {
TestGeomReader()
}
}