How to make SwiftUI Picker take the whole free space in HStack?

232 Views Asked by At

I need to make an input field in which I use standard SwiftUI Picker. Here is my view:

struct CustomPickerRowView<Content:View>: View {
    
    let icon: String
    
    let picker: () -> Content
    
    var body: some View {
        HStack {
            ZStack {
                K.Gradients.mainGradient
                
                Image(systemName: icon)
                    .foregroundStyle(.white.opacity(0.8))
                    .font(.title3)
                    .fontWeight(.bold)
            }
            .frame(width: 45, height: 45)
            
            picker()
                .offset(x: -10)
            
            Spacer()
        }
        .frame(height: 45)
        .background(.textfieldAppearance)
        .clipShape(RoundedRectangle(cornerRadius: 12))
        .shadow(color: .textfieldBg.opacity(0.3), radius: 3, x: 2, y: 2)
    }
}

And I use it like this:

CustomPickerRowView(icon: "figure.run") {
    Picker("", selection: $activityLevel) {
        ForEach(ActivityLevel.allCases, id: \.self) { level in
            Text("\(level.rawValue)".camelCaseToWords())
        }
    }
    .tint(activityLevel == .SelectActivity ? .textGray : .text)
}

I need to make this picker take the full free space and also be clickable everywhere I press on it.

I tried to make it take the full free space with the maxWidth modifier:

.frame(maxWidth: .infinity)

But the clickable area is not covering the full picker width

1

There are 1 best solutions below

0
Benzy Neez On

I found that if you pad the picker labels with spaces then the Picker uses all the space available and it is sensitive to tap across the full width. Fortunately, there don't seem to be any negative consequences of adding more padding than needed, so you can add a lot to be sure of filling the full width. In particular, you don't see any ellipsis appearing.

I think you were trying to use .offset to remove the spacing between the items in the HStack. I would suggest supplying spacing: 0 to the HStack to achieve this. You might not need the Spacer any more either.

Here is your example with the updates applied (and gaps improvised):

enum ActivityLevel: String, CaseIterable {
    case selectActivity = "Select Activity"
    case walk = "Walk"
    case run = "Run"
}

struct CustomPickerRowView<Content:View>: View {

    let icon: String
    let picker: () -> Content

    var body: some View {
        HStack(spacing: 0) {
            ZStack {
//                K.Gradients.mainGradient
                LinearGradient(
                    colors: [Color(white: 0.8), Color(white: 0.3)],
                    startPoint: .top,
                    endPoint: .bottom
                )
                Image(systemName: "figure.walk") // icon
                    .foregroundStyle(.white.opacity(0.8))
                    .font(.title3)
                    .fontWeight(.bold)
            }
            .frame(width: 45, height: 45)

            picker()

            Spacer()
        }
        .frame(height: 45)
        .background(Color(white: 0.9)) // .textfieldAppearance
        .clipShape(RoundedRectangle(cornerRadius: 12))
        .shadow(color: .gray, radius: 3, x: 2, y: 2) // .textfieldBg.opacity(0.3)
    }
}

struct ContentView: View {
    @State private var activityLevel = ActivityLevel.selectActivity
    let padding = String(repeating: " ", count: 1000)

    var body: some View {
        CustomPickerRowView(icon: "figure.run") {
            Picker("", selection: $activityLevel) {
                ForEach(ActivityLevel.allCases, id: \.self) { level in
                    Text("\(level.rawValue)" + padding).tag(level)
                }
            }
            .tint(.primary)
//            .tint(activityLevel == .selectActivity ? .textGray : .text)
        }
    }
}

ActivityPicker