2 ways:
- put "@State private var isPresented", ".swipeActions" and ".confirmationDialog" into list item, just like Esera's answer. But there is a problem, confirmationDialog often popover and disappear in a second, then popvoer when swipeActions before trash button clicked. After some research, I found it's because swipeActions-.destructive button. use .none instead, everything is fine. see ConfirmationDialog0.
- use @State private var selectedMessage: Int? = nil, set it in swipeActions, use it in confirmationDialog. Problem is this confirmationDialog for whole list, not for a list item, just like Esera's answer.
check my test code:
struct ConfirmationDialog: View {
var body: some View {
ScrollView {
ConfirmationDialog3()
ConfirmationDialog0()
ConfirmationDialog1()
ConfirmationDialog2()
}
}
}
struct ConfirmationDialog3: View {
@State private var messages: [Int] = (0..<5).map { $0 }
@State private var confirmationShown = false
@State private var selectedMessage: Int? = nil
var body: some View {
NavigationView {
List {
ForEach(messages, id: \.self) { message in
Text("\(message)")
.swipeActions {
Button(
role: .destructive,
action: {
selectedMessage = message
confirmationShown = true
}
) {
Image(systemName: "trash")
}
}
}
}
.navigationTitle("YEAH!")
.confirmationDialog(
"Are you sure?",
isPresented: $confirmationShown,
titleVisibility: .visible,
presenting: selectedMessage
) { message in
Button("Yes, delete: \(message)") {
withAnimation {
messages = messages.filter { $0 != message }
}
}.keyboardShortcut(.defaultAction)
Button("No", role: .cancel) {}
} message: { message in
Text("\(message)")
}
}
}
}
struct ConfirmationDialog0: View {
@State private var messages: [Int] = (0..<5).map { $0 }
var body: some View {
NavigationView {
List {
ForEach(messages, id: \.self) { message in
itemView0(message: message) { _ in
withAnimation {
messages = messages.filter { $0 != message }
}
}
}
}
.navigationTitle("WRONG if .destructive!")
}
}
}
struct itemView0: View {
@State private var confirmationShown = false
let message: Int
let onDelete: (Int) -> Void
var body: some View {
Text("\(message)")
.swipeActions {
Button(
role: .none,//.destructive,
action: { confirmationShown = true }
) {
Image(systemName: "trash")
}
.tint(Color.red)
}
.confirmationDialog(
"Are you sure?",
isPresented: $confirmationShown,
titleVisibility: .visible,
presenting: message
) { message in
Button("Yes, delete: \(message)") {
onDelete(message)
}.keyboardShortcut(.defaultAction)
Button("No", role: .cancel) {}
} message: { message in
Text("\(message)")
}
}
}
struct ConfirmationDialog1: View {
@State private var messages: [Int] = (0..<5).map { $0 }
@State private var confirmationShown = false
var body: some View {
NavigationView {
List {
ForEach(messages, id: \.self) { message in
Text("\(message)")
.swipeActions {
Button(
role: .destructive,
action: { confirmationShown = true }
) {
Image(systemName: "trash")
}
}
.confirmationDialog(
"Are you sure?",
isPresented: $confirmationShown,
titleVisibility: .visible,
presenting: message
) { message in
Button("Yes, delete: \(message)") {
withAnimation {
messages = messages.filter { $0 != message }
}
}.keyboardShortcut(.defaultAction)
Button("No", role: .cancel) {}
} message: { message in
Text("\(message)")
}
}
}
.navigationTitle("WRONG!")
}
}
}
struct ConfirmationDialog2: View {
@State private var showingConfirmation = false
@State private var backgroundColor = Color.white
var body: some View {
Text("Hello, World!")
.frame(maxWidth: .infinity)
.frame(height: 100)
.background(backgroundColor)
.onTapGesture {
showingConfirmation = true
}
.confirmationDialog("Change background", isPresented: $showingConfirmation) {
Button("Red") { backgroundColor = .red }
Button("Green") { backgroundColor = .green }
Button("Blue") { backgroundColor = .blue }
Button("Cancel", role: .cancel) { }
} message: {
Text("Select a new color")
}
}
}
struct ConfirmationDialog_Previews: PreviewProvider {
static var previews: some View {
ConfirmationDialog()
}
}