Ditza
Ditza

Reputation: 345

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 ()
  
                                }
                            }
                        )
                    }
                }
            }
        }
    }
}

Upvotes: 1

Views: 2410

Answers (2)

mike.bulgakov
mike.bulgakov

Reputation: 31

Just add .onTapGesture under the Button and move action there.

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 {
            Section (header: Text("Animales")) {
                VStack {
                    
                    Picker (selection: $selectedAnimal) {
                        ForEach(Animal.allCases, id:\.self) {
                            Text($0.rawValue)
                        }
                    } label: {
                        HStack {
                            Text ("Chose Animale")
                            Spacer ()
                            Button {} label: {
                                Image(systemName: "arrow.clockwise")
                            }
                            .onTapGesture {
                                print("clicked")
                            }
                            Spacer ()
                        }
                    }
                }
            }
        }
    }
}

#Preview {
    ContentView()
}

Upvotes: 0

Asperi
Asperi

Reputation: 257533

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 !!
            }
        }
    }
}

Upvotes: 1

Related Questions