Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get selected item in SwiftUI list without using a navigation link

Tags:

swiftui

I'm writing a SwiftUI Mac app that is similar to a kanban board. The app has three lists: Todo, Doing, and Done. At the bottom of each list is a button to move a task to another list. For example the todo list has a Start Doing button. Selecting a task from the todo list and clicking the button should move the task from the todo list to the doing list.

Every SwiftUI list selection example I have seen uses a navigation link. Selecting a list item takes you to another view. But I don't want to want to navigate to another view when selecting a list item. I want the selected task so I can change its status and move it to the correct list when clicking the button.

Here's the code for one of my lists.

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}
    }
}

When I click on a list item, it is not selected.

How do I get the selected item from the list without using a navigation link?

Workaround

Tamas Sengel's answer led me to a workaround. Give each list item a Start Doing button so I don't have to track the selection.

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

The workaround helps for my specific case. But I'm going to keep the question open in hopes of an answer that provides a better alternative to using a button for people who want a way to get the selected list item.

like image 212
Swift Dev Journal Avatar asked Jan 20 '26 17:01

Swift Dev Journal


2 Answers

Use a Button in the List and in the action, set a @State variable to the current list item.

@State var currentTask: Task?

List(todoTasks, id: \.self) { task in
    Button {
        currentTask = task
    } label: {
        Text(task.title)
    }
}
like image 140
Tamás Sengel Avatar answered Jan 23 '26 12:01

Tamás Sengel


Use .environment(\.editMode, .constant(.active)) to turn on selecting capability.

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()
    }
}
like image 20
Paul B Avatar answered Jan 23 '26 12:01

Paul B



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!