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)
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)
Share
Improve this question
edited Nov 19, 2024 at 13:15
Benzy Neez
24.1k3 gold badges16 silver badges46 bronze badges
asked Nov 19, 2024 at 13:01
loonnloonn
31 bronze badge
1
|
1 Answer
Reset to default 0This 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
. - It doesn't work to use the text labels themselves as the source for
.matchedGeometryEffect
, but it does work to use invisible placeholders in the background. - Use the ids of the picker options as the ids for matching.
- Apply a
.contentShape
to the clear overlay, to make it receptive to tap gestures.
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 = ""
}
}
}
}
}
UISegmentedControl
(see here) and write aUIViewRepresentable
wrapping it, or build your own picker from scratch. – Sweeper Commented Nov 19, 2024 at 13:06