UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

`selection:` binding in `List`

Forums > SwiftUI

@roosterboy kindly fixed my enum issue with RawRepresentable, but now I'm really struggling to understand how the List(selection:) causes an update to the selection binding.

I am unpicking the Apple sample code at https://developer.apple.com/documentation/swiftui/building_a_great_mac_app_with_swiftui

There is a sidebar on the left, and the main detail view on the right. The ContentView looks like this:

struct ContentView: View {
    @EnvironmentObject var store: Store
    @SceneStorage("selection") private var selectedGardenID: Garden.ID?
    @AppStorage("defaultGarden") private var defaultGardenID: Garden.ID?

    var body: some View {
        NavigationView {
            Sidebar(selection: selection)
            GardenDetail(garden: selectedGarden)
        }
    }

    private var selection: Binding<Garden.ID?> {
        Binding(get: { selectedGardenID ?? defaultGardenID }, set: { selectedGardenID = $0 })
    }

    private var selectedGarden: Binding<Garden> {
        $store[selection.wrappedValue]
    }
}

and the Sidebar like this:

struct Sidebar: View {
    @EnvironmentObject var store: Store
    @SceneStorage("expansionState") var expansionState = ExpansionState()
    @Binding var selection: Garden.ID?

    var body: some View {
        List(selection: $selection) {
            DisclosureGroup(isExpanded: $expansionState[store.currentYear]) {
                ForEach(store.gardens(in: store.currentYear)) { garden in
                    SidebarLabel(garden: garden)
                        .badge(garden.numberOfPlantsNeedingWater)
                }
            } label: {
                Label("Current", systemImage: "chart.bar.doc.horizontal")
            }

            Section("History") {
                GardenHistoryOutline(range: store.previousYears, expansionState: $expansionState)
            }
        }
        .frame(minWidth: 250)
    }
}

Now the SidebarLabel view does not have any reference to the selection binding; yet, when you click on one of the labels, the selection updates and the binding propagates up to the ContentView and therefore the GardenDetai view is updated with the new selection.

My question is: how does clicking on one of the sidebar items cause this to happen, since there is no reference to the selection binding? Does the ForEach have anything to do with it?

If so, how can I add 'ad hoc' items that do not particpate in a ForEach like this? If not, what is going on??

3      

In a way yes the ForEach does have something to do with it, because it is accessing store.gardens where store is an @EnvironmentObject, ie. something that can be accessed throughout the app's environment (where there is a suitable declaration).

EnvironmentObjects are linked to ObservableObject declarations, so any View accessing the EnvironmentObject is observing the object for changes, hence when you change it in one View, it is updated everywhere else it is accessed.

garden is just an instance of the ForEach loop, and SidebarLabel, updates garden through the Binding.

2      

My question is: how does clicking on one of the sidebar items cause this to happen, since there is no reference to the selection binding? Does the ForEach have anything to do with it?

Behind the scenes, ForEach assigns a tag to each View it generates, based on the id provided, whether through an explicit id parameter or because the items being looped through conform to Identifiable. Then the Picker assigns the id of whichever View was selected to the bound selection variable. This is why the type of the tag, which cann either be assigned implicitly by the ForEach or explicitly using the .tag(_:) modifier, must match the type of the bound selection parameter.

3      

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.