Search code examples
swiftuipicker

Add button to picker label


I want to add a button to swiftui picker's label.
But the button is not clickable.
When I click on the button the picker is clicked.
How Do I make the picker take clicks only in the area of the selected value?
and the buttons take his clicks?

import SwiftUI


enum Animal: String, CaseIterable, Identifiable {
    case dog
    case cat
    case bird
    var id: String { self.rawValue }
}

struct ContentView: View {
    @State private var selectedAnimal = Animal.dog
    
    var body: some View {
        Form {
            Group {

                Section(header: Text("Animales")) {
                    VStack{

                        Picker(
                            selection: $selectedAnimal,
                            content: {
                                ForEach(Animal.allCases, id:\.self) {
                                    Text($0.rawValue)
                                }},
                            label: {
                                HStack {
                                    Text ("Chose Animale")
                                    Spacer ()
                                    Button (
                                        action: {
                                            print ("clicked")
                                        },
                                        label: {
                                            Image(systemName: "arrow.clockwise")
                                        })
                                    
                                    Spacer ()
  
                                }
                            }
                        )
                    }
                }
            }
        }
    }
}

Solution

  • To solve this issue we need to separate picker and button and block Form tracking click inside row (which is by default track entire row).

    For first move button out of picker and place everything in HStack, for second we need couple of tricks like tapGesture on label and non-default button style for button (for simplicity I used primitive button style, but it's better to create custom with appropriate highlight, etc.)

    demo

    Here is a simplified updated and tested your code (Xcode 13 / iOS 15):

    var body: some View {
        Form {
            Group {
                Section(header: Text("Animales")) {
                    HStack{
                        HStack {
                            Text ("Chose Animale")
                            Spacer ()
                        }
                        .contentShape(Rectangle())
                        .onTapGesture {
                           // just blocker for label click
                        }
                        .overlay(
                            Button (
                                action: {
                                    print ("clicked")
                                },
                                label: {
                                    Image(systemName: "arrow.clockwise").foregroundColor(.blue)
                                })
                                .buttonStyle(PlainButtonStyle())   // << needed custom !!
                        )
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .layoutPriority(1)     // << to cover much area
                        //.border(Color.red)      // << for testing area
    
                        Picker("",
                                 selection: $selectedAnimal,
                                 content: {
                            ForEach(Animal.allCases, id:\.self) {
                                Text($0.rawValue)
                            }}
                        )
                            .labelsHidden()   // << hide own label
                            .fixedSize()      // << limit size !!
                    }
                    .listRowInsets(EdgeInsets()) // << consume row space !!
                }
            }
        }
    }