I was playing with Swift Playground with the following code.
What I was doing is to modify a @State
variable in a Button
action, and then show its current value in a full-screen sheet.
The problem is that, notice the code line I commented, without this line, the value displayed in the full-screen sheet will be still 1, with this line, the value will be 2 instead, which is what I expected it to be.
I want to know why should this happen. Why the Text
matters.
import SwiftUI
struct ContentView: View {
@State private var n = 1
@State private var show = false
var body: some View {
VStack {
// if I comment out this line, the value displayed in
// full-screen cover view will be still 1.
Text("n = \(n)")
Button("Set n = 2") {
n = 2
show = true
}
}
.fullScreenCover(isPresented: $show) {
VStack {
Text("n = \(n)")
Button("Close") {
show = false
// UPDATE(1)
print("n in fullScreenCover is", n)
}
}
}
}
}
Playground Version: Version 4.1 (1676.15)
Update 1:
As Asperi answered, if n
in fullScreenCover
isn't captured because it's in different contexts (closure?), then why does the print
line prints n in fullScreenCover is 2
when not put Text
in body
?
CodePudding user response:
The fullScreenCover
(and similarly sheet
) context is different context, created once and capturing current states. It is not visible for view's dynamic properties.
With put Text dependable on state into body just makes all body re-evaluated once state changed, thus re-creating fullScreenCover with current snapshot of context.
A possible solutions:
- to capture dependency explicitly, like
.fullScreenCover(isPresented: $show) { [n] in // << here !!
VStack {
Text("n = \(n)")
to make separated view and pass everything there as binding, because binding is actually a reference, so being captured it still remains bound to the same source of truth:
.fullScreenCover(isPresented: $show) { FullScreenView(n: $n, show: $show) }
and view
struct FullScreenView: View {
@Binding var n: Int
@Binding var show: Bool
var body: some View {
VStack {
Text("n = \(n)")
Button("Close") {
show = false
}
}
}
}
both give:
Tested with Xcode 13.4 / iOS 15.5
CodePudding user response:
For passing data into fullScreenCover
we use a different version which is fullScreenCover(item:onDismiss:content:)
. There is an example at that link but in your case it would be:
struct FullscreenCoverTest: View {
@State private var n = 1
@State private var coverData: CoverData?
var body: some View {
VStack {
Button("Set n = 2") {
n = 2
coverData = CoverData(n: n)
}
}
.fullScreenCover(item: $coverData) { item in
VStack {
Text("n = \(item.n)")
Button("Close") {
coverData = nil
}
}
}
}
}
struct CoverData: Identifiable {
let id = UUID()
var n: Int
}
Note when it's done this way, if the sheet is open when coverData
is changed to one with a different ID, the sheet will animate away and appear again showing the new data.