在不使用导航链接的情况下获取 SwiftUI 列表中的选定项目

Posted

技术标签:

【中文标题】在不使用导航链接的情况下获取 SwiftUI 列表中的选定项目【英文标题】:Get selected item in SwiftUI list without using a navigation link 【发布时间】:2021-08-09 18:49:15 【问题描述】:

我正在编写一个类似于看板的 SwiftUI Mac 应用程序。该应用程序具有三个列表:Todo、Doing 和 Done。每个列表的底部是一个按钮,用于将任务移动到另一个列表。例如,待办事项列表有一个开始执行按钮。从待办事项列表中选择一项任务并单击该按钮应将任务从待办事项列表移动到正在执行的列表中。

我见过的每个 SwiftUI 列表选择示例都使用导航链接。选择一个列表项会将您带到另一个视图。但我不想在选择列表项时导航到另一个视图。我想要选定的任务,以便在单击按钮时更改其状态并将其移动到正确的列表中。

这是我的一份清单的代码。

struct TodoList: View 
    // The board has an array of tasks.
    @Binding var board: KanbanBoard
    @State private var selection: Task? = nil
    @State private var showAddSheet = false
    
    var body: some View 
        VStack 
            Text("Todo")
                .font(.title)
            List(todoTasks, selection: $selection)  task in
                Text(task.title)
            
            HStack 
                Button(action:  showAddSheet = true , label: 
                    Label("Add", systemImage: "plus.square")
                )
                Spacer()
                Button(action:  selection?.status = .doing, label: 
                    Label("Start Doing", systemImage: "play.circle")
                )
            
            
        
        .sheet(isPresented: $showAddSheet) 
            AddTaskView(board: $board)
        
    
    
    var todoTasks: [Task] 
       // Task conforms to Identifiable. 
       // A task has a status that is an enum: todo, doing, or done.
        return board.tasks.filter  $0.status == .todo
    

当我点击一个列表项时,它没有被选中。

如何在不使用导航链接的情况下从列表中获取所选项目?

解决方法

Tamas Sengel 的回答让我找到了解决方法。为每个列表项提供一个开始执行按钮,这样我就不必跟踪选择。

List(todoTasks, id: \.self)  task in
    HStack 
        Text(task.title)
        Button 
            task.status = .doing
         label: 
            Text("Start Doing")
        
    
                

解决方法有助于我的具体情况。但我将保持这个问题的开放性,希望得到一个答案,为想要获取所选列表项的人提供更好的选择,而不是使用按钮。

【问题讨论】:

【参考方案1】:

在列表和操作中使用按钮,将@State 变量设置为当前列表项。

@State var currentTask: Task?

List(todoTasks, id: \.self)  task in
    Button 
        currentTask = task
     label: 
        Text(task.title)
    

【讨论】:

【参考方案2】:

使用.environment(\.editMode, .constant(.active)) 开启选择功能。

import SwiftUI

struct ContentView: View 
    struct Ocean: Identifiable, Hashable 
        let name: String
        let id = UUID()
    
    private var oceans = [
        Ocean(name: "Pacific"),
        Ocean(name: "Atlantic"),
        Ocean(name: "Indian"),
        Ocean(name: "Southern"),
        Ocean(name: "Arctic")
    ]
    @State private var multiSelection = Set<UUID>()
    
    var body: some View 
        NavigationView 
            List(oceans, selection: $multiSelection) 
                Text($0.name)
            
            .navigationTitle("Oceans")
            .environment(\.editMode, .constant(.active))
            .onTapGesture 
                // This is a walk-around: try how it works without `asyncAfter()`
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.05, execute: 
                    print(multiSelection)
                )
            
        
        Text("\(multiSelection.count) selections")
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

【讨论】:

以上是关于在不使用导航链接的情况下获取 SwiftUI 列表中的选定项目的主要内容,如果未能解决你的问题,请参考以下文章

如何在不中断删除的情况下在 SwiftUI 中实现 TextField 列表

如何在不使用swiftUI附带的幻灯片删除的情况下创建自定义删除按钮我没有使用列表,只是使用foreach循环

SwiftUI:如何让列表视图中的图像与列表项的操作不同?

如何在不使用任何 ID 的情况下从 spotify API 获取艺术家列表

SwiftUI 导航链接

SwiftUI 如何创建一个将项目添加到包含导航链接的列表的按钮