如何在 SwiftUI 中保持 SwipeActions 打开?

问题描述 投票:0回答:1

我正在尝试使用

swipeActions
在我的 SwiftUI 应用程序中实现删除功能。当用户在列表项上滑动时,他们应该会看到删除按钮。点击此按钮应在删除项目之前显示确认警报。

但是,我面临一个问题,即一旦出现警报,滑动操作就会自动关闭。这会导致列表项滑回其原始位置,从而不清楚哪个项目将被删除。

我希望在显示警报时滑动操作保持打开状态,以便用户可以清楚地看到他们要确认删除的项目。

这是演示问题的最小代码:

import SwiftUI

struct ContentView: View {
    @State private var numbers = Array(1...10)
    @State private var showingDeleteAlert = false
    @State private var itemToDelete: Int?
    
    var body: some View {
        List {
            ForEach(numbers, id: \.self) { number in
                Text("\(number)")
                    .swipeActions {
                        Button(role: .none) {
                            itemToDelete = number
                            showingDeleteAlert = true
                        } label: {
                            Label("Delete", systemImage: "trash")
                        }
                        .tint(.red)
                    }
            }
        }
        .alert("Confirm", isPresented: $showingDeleteAlert) {
            Button("Cancel", role: .cancel) {}
            Button("Delete", role: .destructive) {
                deleteItem()
            }
        } message: {
            Text("Are you sure you want to delete this item?")
        }
    }
    
    private func deleteItem() {
        if let index = numbers.firstIndex(of: itemToDelete ?? 0) {
            numbers.remove(at: index)
        }
        itemToDelete = nil
    }
}

问题:

  • 点击滑动操作中的删除按钮时,
    showingDeleteAlert
    会设置为
    true
    ,并且会出现警报。
  • 一旦显示警报,滑动操作就会关闭,列表项会向后滑动,从而不清楚哪个项目正在被删除。

问题:

  • 如何在显示确认警报时保持滑动操作打开?

作为参考,我附上了一个 GIF 来说明所需的行为。 enter image description here

任何指导或解决方案将不胜感激!

swiftui swiftui-list swiftui-swipeactions
1个回答
0
投票

与可以使用

.menuActionDismissBehavior
在选择后保持菜单打开的菜单和选择器不同,我不知道有类似的修改器可以与
.swipeActions
配合使用。

但是,请注意,当滑动按钮显示时,内容将移出视图并且不再可见。因此,保持按钮可见“以便用户可以清楚地看到他们要确认删除的项目”的目标变得毫无意义,因为该项目根本不可见(取决于内容的类型和长度) 。这在你的 GIF 和代码示例中很明显。

因此,您可能需要采取另一种方法,通过在警报/确认消息中包含项目详细信息,让用户清楚地确认他们要删除的项目。

您可以通过使用

.alert
presenting
参数
来完成此操作。由于您已经设置了
itemToDelete
,所以这非常简单:

.alert("Confirm", isPresented: $showingDeleteAlert, presenting: itemToDelete) { item in
            Button("Cancel", role: .cancel) {}
            Button("Delete", role: .destructive) {
                deleteItem()
            }
        } message: { item in
            Text("Are you sure you want to delete item \(item)?")
        }

但是,为了避免项目设置和警报显示之间潜在的竞争问题,您可能希望将警报显示为设置要删除的项目的结果,而不是在设置

 后立即切换 
showingDeleteAlert
 itemToDelete

完整代码如下:

import SwiftUI

struct SwipeActionDeleteConfirm: View {
    @State private var numbers = Array(1...10)
    @State private var showingDeleteAlert = false
    @State private var itemToDelete: Int?
    
    var body: some View {
        List {
            ForEach(numbers, id: \.self) { number in
                Text("\(number)")
                    .swipeActions {
                        Button {
                            itemToDelete = number
                            // showingDeleteAlert = true // <- remove this line
                        } label: {
                            Label("Delete", systemImage: "trash")
                        }
                        .tint(.red)
                    }
            }
        }
        .onChange(of: itemToDelete){
            if itemToDelete != nil {
                showingDeleteAlert = true // <- show alert when item changes and is not nil
            }
        }
        
        //Use .alert with presenting parameter that provides an action closure parameter (item)
        .alert("Confirm", isPresented: $showingDeleteAlert, presenting: itemToDelete) { item in // <- note the closure parameter
            Button("Cancel", role: .cancel) {
                itemToDelete = nil // <- reset itemToDelete on cancel
            }
            Button("Delete", role: .destructive) {
                withAnimation {
                    deleteItem() // <- optional - delete with animation for a smoother update of the list
                }
            }
        } message: { item in // <- note the closure parameter
            Text("Are you sure you want to delete item \(item)?") // <- include the item details/name
        }
    }
    
    private func deleteItem() {
        if let index = numbers.firstIndex(of: itemToDelete ?? 0) {
                numbers.remove(at: index)
        }
        itemToDelete = nil
    }
}

#Preview {
    SwipeActionDeleteConfirm()
}

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.