I have a simple SwiftUI app with a list of 2 items: Item 1 and item 2. Tapping on one of the items should show the corresponding page:
ContentView.swift
struct ContentView: View {
@State var isPresented:Bool = false
@State var items = [
"item 1",
"item 2",
]
var body: some View {
List {
ForEach(items, id:\.self) { item in
Button {
isPresented = true
} label: {
Text(item)
}
.sheet(isPresented: $isPresented, content: {
CustomView(text: item)
})
}
.onDelete { indexSet in
items.remove(atOffsets: indexSet)
}
}
}
}
CustomView.swift
struct CustomView: View {
var text:String
var body: some View {
Text("Text: \(text)")
.onAppear {
print("text: \(text)")
}
}
}
The expected behavior with this code is tapping that Item 1 or Item 2 presents a sheet with a label reading "item 1" or "Item 2", respectively.
However, that is not what happens. Tapping on any of the items always shows item 2's page. Even after deleting item 2 and tapping item 1 again, the page always shows item 2.
This issue persisted from iOS 15 to iOS 16 (I didn't test it before iOS 15). Running on various simulators and physical devices, including iPhone 11 Pro, and iPhone 14 Pro. Xcode version 14.0, macOS 12.6.
Is this a bug? Or is my code wrong? Any help would be appreciated.
You need to move the sheet
modifier outside of the ForEach
, as it's currently creating a sheet for every item bound to the same isPresented
variable. When you set isPresented
to true
, you are essentially showing each item's sheet, but you only see the last one.
You can instead use the sheet(item:onDismiss:content:)
modifier.
Something like this will work:
// the item parameter requires the data to be Identifiable
// you can make a custom struct to hold this, or manually conform String to Identifiable
struct Item: Identifiable {
let id = UUID()
let name: String
}
// you can remove the isPresented variable
@State selectedItem: Item?
var body: some View {
...
ForEach(...) { item in
Button {
selectedItem = Item(name: item)
} label: {
...
}
}
// move outside loop so there is only one sheet that shows a view dependant on the selectedItem
.sheet(item: $selectedItem) { item in
CustomView(text: item)
}
...
}