In the following code, I have an optional @State
variable call isReseting
which I set to true
with a tap of a button. The button also triggers a sheet when it's tapped, which is working fine but what doesn't work is that if I read the value of the isResting
variable inside the .sheet
it outputs as nil
and I'm expecting that to be true
since it was set to true
when the button was tapped.
Any idea why the value of the isReseting
variable becomes nil
inside the .sheet
?
struct Item: Identifiable{
var id = UUID()
var name:String
var active:Bool
}
struct ContentView: View {
var items:[Item] = [Item(name: "Oranges", active: true),
Item(name: "Apples", active: false),
Item(name: "Cookies", active: false) ]
@State private var selectedItem: Item?
@State private var isReseting: Bool?
var body: some View {
List{
ForEach(items){ item in
HStack{
Text(item.name)
Button(item.active ? "Reset": "Initiate"){
selectedItem = item
isReseting = true
let _ = print("Value after button tap: \(isReseting)")// output: Value after button tap: Optional(true)
}
.padding()
.background(item.active ? Color.blue: Color.gray)
.foregroundColor(.white)
.cornerRadius(30)
.frame(width: 100, height: 65)
}
}
}
.sheet(item: $selectedItem){ item in
// I'm expecting isReseting to be true here, but it's nil
let _ = print("Value in sheet: \(isReseting)")// outputs: Value in sheet: nil
if isReseting == true {
Text("It's reseting!!!")
}else{
Text("It's NOT reseting")
}
}
}
}
CodePudding user response:
I think you're getting caught by the fact that sheet(item:)
only re-renders based on item
-- not the encapsulating View's @State
variables. As long as you explicitly pass isReseting
through the item
, it will work:
struct Item: Identifiable, Equatable {
var id = UUID()
var name:String
var active:Bool
}
struct ContentView: View {
var items:[Item] = [Item(name: "Oranges", active: true),
Item(name: "Apples", active: false),
Item(name: "Cookies", active: false) ]
struct SheetItem : Identifiable {
var item: Item
var isReseting: Bool
var id: UUID {
self.item.id
}
}
@State private var selectedItem: SheetItem?
@State private var isReseting: Bool?
var body: some View {
List{
ForEach(items){ item in
HStack{
Text(item.name)
Button(item.active ? "Reset": "Initiate"){
isReseting = true
selectedItem = SheetItem(item: item, isReseting: isReseting ?? false)
}
.padding()
.background(item.active ? Color.blue: Color.gray)
.foregroundColor(.white)
.cornerRadius(30)
.frame(width: 100, height: 65)
}
}
}
.sheet(item: $selectedItem) { item in
let _ = print("Value in sheet: \(item.isReseting)")
if item.isReseting == true {
Text("It's reseting!!!")
} else {
Text("It's NOT reseting")
}
}
}
}
A second way to accomplish this is explicitly pass a Binding
(even though you don't need to mutate it in the sheet), since it will maintain its link to the @State
variable:
struct Item: Identifiable{
var id = UUID()
var name:String
var active:Bool
}
struct ContentView: View {
var items:[Item] = [Item(name: "Oranges", active: true),
Item(name: "Apples", active: false),
Item(name: "Cookies", active: false) ]
@State private var selectedItem: Item?
@State private var isReseting: Bool?
var body: some View {
List{
ForEach(items){ item in
HStack{
Text(item.name)
Button(item.active ? "Reset": "Initiate"){
selectedItem = item
isReseting = true
let _ = print("Value after button tap: \(isReseting)")// output: Value after button tap: Optional(true)
}
.padding()
.background(item.active ? Color.blue: Color.gray)
.foregroundColor(.white)
.cornerRadius(30)
.frame(width: 100, height: 65)
}
}
}
.sheet(item: $selectedItem){ item in
// I'm expecting isReseting to be true here, but it's nil
SheetView(isReseting: $isReseting)
}
}
}
struct SheetView : View {
@Binding var isReseting : Bool?
var body: some View {
let _ = print("Value in sheet: \(isReseting)")// outputs: Value in sheet: nil
if isReseting == true {
Text("It's reseting!!!")
}else{
Text("It's NOT reseting")
}
}
}