swiftswiftuipicker

SwiftUI Segmented Control Picker


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.

    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 = ""
                        }
                }
            }
        }
    }
    

    Animation