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:
- 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
}
}
}
- you don't need the
selection
getter/setter. You can use@SceneStorage
directly, 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 })
// }
}