Lumentus
Lumentus

Reputation: 91

Multiline label for SwiftUI Picker SegmentedPickerStyle

Is there a way to create a multiline label in a SwiftUI Picker with a SegementedPickerStyle. I basically would like to create the following:

targetView

But I have not found a way to create a Picker with a multiline label. Is there a way to do this with standard SwiftUI controls? From my research even in UIKit this was a problem.

Upvotes: 2

Views: 1449

Answers (1)

Asperi
Asperi

Reputation: 258345

System controls are only default controls - we can always implement our own with whichever parameters we need.

Here is simplified demo of how to implement custom picker with any cell content needed.

Note: all constants and parameters can be separated into standalone configuration struct to be used as style, etc.

Used Xcode 13.2 / iOS 15.2

demo

struct TestMyPicker: View {

    @State private var selection: Int?
    let data = [1, 2, 3, 4]
    var body: some View {
        MyPicker(data: data, selection: $selection) { item in
            VStack {
                Text("H\(item)")
                Text("Sub \(item)")
            }.padding(.horizontal, 12)
        }
    }
}

extension Int: Identifiable {      // just for testing purpose
    public var id: Int { self }
}


struct MyPicker<Cell, Data>: View where Cell: View, Data: RandomAccessCollection, Data.Element: Identifiable {
    let data: Data
    @Binding var selection: Data.Element?
    @ViewBuilder let cell: (Data.Element) -> Cell
    @Namespace private var ns

    var body: some View {
        HStack(spacing: 0) {
            ForEach(data) { item in
                cell(item)
                    .foregroundColor(.white)
                    .padding(.horizontal, 8)
                    .overlay(
                        Group {
                            if selection?.id == item.id {
                                RoundedRectangle(cornerRadius: 8).fill(Color.white)
                                    .opacity(0.2)
                                    .matchedGeometryEffect(id: "Marker", in: ns)
                            }
                        }
                    )
                    .onTapGesture {
                        if selection?.id == item.id {
                            selection = nil
                        } else {
                            selection = item
                        }
                    }
            }
        }
        .animation(.linear(duration: 0.25), value: selection?.id)
        .fixedSize(horizontal: false, vertical: true)
        .padding(2)
        .background(RoundedRectangle(cornerRadius: 8).fill(Color.gray))
    }
}

Upvotes: 5

Related Questions