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