I’m trying to implement a segmented picker with an additional behavior where tapping the selected option again deselects it. However, this code doesn’t seem to function as intended.
Picker("",selection: $selectedOption) {
ForEach(["A","B","C","D","E"], id:\.self) { option in
Text(option)
.onTapGesture {
if selectedOption == option {
selectedOption = ""
}
}
}
}
.pickerStyle(.segmented)
>Solution :
This kind of behavior can be achieved by superimposing the selected option with an overlay and attaching the tap gesture to the overlay. When tapped, the selection can be de-selected.
One way to get the positioning right is to use .matchedGeometryEffect for placeholders in the background of the Picker. The ids used for matching should match the ids of the picker options:
struct ContentView: View {
let options = ["A", "B", "C", "D", "E"]
@State private var selectedOption = ""
@Namespace private var ns
var body: some View {
Picker("", selection: $selectedOption) {
ForEach(options, id:\.self) { option in
Text(option)
}
}
.pickerStyle(.segmented)
.background {
// A row of placeholders
HStack(spacing: 0) {
ForEach(options, id: \.self) { option in
Color.clear
.matchedGeometryEffect(id: option, in: ns, isSource: true)
}
}
}
.overlay {
if selectedOption != "" {
Color.clear
.contentShape(Rectangle())
.matchedGeometryEffect(id: selectedOption, in: ns, isSource: false)
.onTapGesture {
selectedOption = ""
}
}
}
}
}
