'SwiftUI List selection binding updates twice on every click, if wrapped in NavigationView (macOS)
Given the below code, I made a strange observation (on macOS). If a List is wrapped in a NavigationView I suddenly get two updates per click on a row (one on mouseDown - aka holding the mouse click down and not releasing it, one on mouseUp - aka releasing the mouse click). This doesn't happen on a plain simple list, or if it's wrapped in an HStack instead. Does anyone know why and how I can control / change this behavior?
See HStack version in action:
See NavigationView version in action:
struct ContentView: View {
@State var selection: Set<Int> = []
var body: some View {
List(0..<20, selection: Binding(get: {
self.selection
}, set: { val in
print("set \(val)")
self.selection = val
})) { idx in
Text("\(idx)")
}
Color.red
Color.green
Color.blue
}
}
// Wrapped in a HStack, 1 update per row selection is triggered, as expected!
struct HStackVersion: View {
var body: some View {
HStack(spacing:0.0) {
ContentView()
}
}
}
// Wrapped in a NavigationView, 2 updates per row selection are triggered??
struct NavigationViewVersion: View {
var body: some View {
NavigationView {
ContentView()
}
}
}
Solution 1:[1]
The screenshot below shows the stacktrace for a breakpoint in the setter showing that SwiftUI.ListCoreCoordinator is setting the binding twice. I've seen a similar problem with Map here. I'm not sure if duplicate calls to binding setters is part of their design or not but it understandably could be, given that SwiftUI coalesces all state changes into a single call to body.
If you'd like to work around the issue then I would suggest going with the more standard onChange modifier instead of that custom Binding, onChange is only called once for the new value, e.g.
struct MacListProbView: View {
@State var selection: Set<Int> = []
var body: some View {
List(0..<20, selection: $selection) { idx in
Text("\(idx)")
}
.onChange(of: selection) { newSelection in
print("set \(newSelection)")
}
}
}
struct MacListProbViewNav: View {
var body: some View {
NavigationView {
MacListProbView()
Color.red // note it's more efficient to have these here because they are not affected by the selection value.
Color.green
Color.blue
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | malhal |



