I'm trying to use anchorPreference to draw an overlay over some view in my VStack.
However, it only works in the simplest cases. When content of my VStack gets a little bit more complex, the overlay is never drawn.
Here's my code
struct debug_test: View {
@State private var someState = false
var body: some View {
    VStack {
        Text("Hello World !!!")
            .anchorPreference(
                key: BoundsPreferenceKey.self,
                value: .bounds
            ) { $0 }
        //////////////////////////////////////////////////////////
        // When I remove below lines - it works ok.
        // But when I put add some conditionally-visible view and one more view
        // it stops drawing an overlay.
        // Apparently, `preferences` is always nil in that case.
        if someState {
            Text("Pooop")
        }
        Text("Pooop2")
        //////////////////////////////////////////////////////////
    }
    .overlayPreferenceValue(BoundsPreferenceKey.self) { preferences in
        GeometryReader { geometry in
            preferences.map {
                Rectangle()
                    .stroke(Color.red, lineWidth: 5)
                    .frame(
                        width: geometry[$0].width,
                        height: geometry[$0].height
                )
                    .offset(
                        x: geometry[$0].minX,
                        y: geometry[$0].minY
                )
            }
        }
    }
  }
 }
As I explained in the code comments, when I get a simple stack with a single view inside, it works fine. But when I add a few more views and some conditionals inside, it stops working. Any clue how to fix it?
Preferences are updated during layout and I assume your preference key held Anchor<CGRect>? which was reset to nil at the end of layout.
Here is fixed variant. Tested with Xcode 11.4 / iOS 13.4.
Note: I would recommend to use explicit struct model for your custom preference key. Consider this solution for example

struct BoundsPreferenceKey: PreferenceKey {
    static var defaultValue: [Anchor<CGRect>] = [] // << use something persistent
    static func reduce(value: inout [Anchor<CGRect>], nextValue: () -> [Anchor<CGRect>]) {
        value.append(contentsOf:nextValue())
    }
}
struct debug_test: View {
@State private var someState = false
var body: some View {
    VStack {
        Text("Hello World !!!")
            .anchorPreference(
                key: BoundsPreferenceKey.self,
                value: .bounds
            ) { [$0] }
        if someState {
            Text("Pooop")
        }
        Text("Pooop2")
        //////////////////////////////////////////////////////////
    }
    .overlayPreferenceValue(BoundsPreferenceKey.self) { preferences in
        GeometryReader { geometry in
            preferences.map {
                Rectangle()
                    .stroke(Color.red, lineWidth: 5)
                    .frame(
                        width: geometry[$0].width,
                        height: geometry[$0].height
                )
                    .position(    // << here is fix as well !!
                        x: geometry[$0].midX,
                        y: geometry[$0].midY
                )
            }[0]     // << just for demo, this should be more safe !!
        }
    }
  }
 }
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