Home > OS >  Cannot select item in List
Cannot select item in List

Time:03-22

I am trying to build a (macOS) view with a sidebar and a main area. The main area contains various custom views rather than a single view with a detail ID. My code looks like this:

enum SidebarTargetID: Int, Identifiable, CaseIterable {
    case status, campaigns

    var id: Int {
        rawValue
    }
}

struct SidebarView: View {

    @Binding var selection: SidebarTargetID?

    var body: some View {
        List(selection: $selection) {
            Label("Status", systemImage: "heart").id(SidebarTargetID.status)
            Label("Campaigns", systemImage: "heart").id(SidebarTargetID.campaigns)
        }
    }

}

struct ContentView: View {

    @SceneStorage("sidebarSelection") private var selectedID: SidebarTargetID?

    var body: some View {
        NavigationView {
            SidebarView(selection: selection)

            switch selectedID {
            case .status: StatusView()
            case .campaigns: CampaignView()
            default: StatusView()
            }
        }
    }

    private var selection: Binding<SidebarTargetID?> {
        Binding(get: { selectedID ?? SidebarTargetID.status }, set: { selectedID = $0 })
    }

}

The sidebar view, however, does not appear to be responding to its items being selected by clicking on them: no seleciton outline, no change of view in the main area.

Why is this? I have seen a ForEach being used in a List of Identifiable objects whose IDs activate the selection binding and do the selection stuff. What am I doing wrong?

EDIT

Tried this too, doesn't work.

enum SidebarTargetID: Int, Identifiable, CaseIterable {
    case status, campaigns

    var id: Int {
        rawValue
    }
}

struct SidebarView: View {

    @Binding var selection: SidebarTargetID?

    let sidebarItems: [SidebarTargetID] = [
        .status, .campaigns
    ]

    var body: some View {
        List(selection: $selection) {
            ForEach(sidebarItems) { sidebarItem in
                SidebarLabel(sidebarItem: sidebarItem)
            }
        }
    }

}

struct SidebarLabel: View {
    var sidebarItem: SidebarTargetID

    var body: some View {
        switch sidebarItem {
        case .status: Label("Status", systemImage: "leaf")
        case .campaigns: Label("Campaigns", systemImage: "leaf")
        }

    }
}

CodePudding user response:

Ok, found the problem:

struct SidebarView: View {

    @Binding var selection: SidebarTargetID?

    var sidebarItems: [SidebarTargetID] = [
        .status, .campaigns
    ]

    var body: some View {
        List(sidebarItems,  id: \.self, selection: $selection) { sidebarItem in
            SidebarLabel(sidebarItem: sidebarItem)
        }
    }

}

CodePudding user response:

Just two little things:

  1. the List items want a .tag not an .id:
struct SidebarView: View {
    
    @Binding var selection: SidebarTargetID?
    
    var body: some View {
        List(selection: $selection) {
            Label("Status", systemImage: "heart")
                .tag(SidebarTargetID.status)  // replace .id with .tag
            Label("Campaigns", systemImage: "heart")
                .tag(SidebarTargetID.campaigns) // replace .id with .tag
        }
    }
    
}
  1. you don't need the selection getter/setter. You can use @SceneStoragedirectly, it is a State var.
struct ContentView: View {
    
    @SceneStorage("sidebarSelection") private var selectedID: SidebarTargetID?
    
    var body: some View {
        NavigationView {
            SidebarView(selection: $selectedID) // replace selection with $selectedID
            
            switch selectedID {
            case .status: Text("StatusView()")
            case .campaigns: Text("CampaignView()")
            default: Text("StatusView()")
            }
        }
    }
    
    // no need for this
//    private var selection: Binding<SidebarTargetID?> {
//        Binding(get: { selectedID ?? SidebarTargetID.status }, set: { selectedID = $0 })
//    }
    
}
  • Related