swiftswiftuiuipickerview

How to make SwiftUI Picker Wrap Text


When using a Picker with .pickerStyle(WheelPickerStyle()), the horizontal space that the picker takes up is seemingly fixed. In the image below, the Picker frame (which is seen by its lighter color) is larger than it needs to be for its elements.

Screenshot

I'm trying to get the picker to only be as wide as it needs to be for the longest option it contains, similar to how Text() views "wrap" their text, and are only as big as the text inside them requires them to be.

How can I accomplish this for a Wheel Picker in SwiftUI?


Solution

  • A solution appears a bit complicated because Picker is not native SwiftUI control, but has UIPickerView at backend, so we need combination of already mentioned one to get rid of compression contains and another one to calculate most long label and compress picker explicitly.

    demo

    Main part is

    @State private var maxWidth = CGFloat.zero
    @State private var width = CGFloat.infinity
    
    var body: some View {
        Picker("", selection: $selection) {
            ForEach( ... 
    
               // Row/Label view is here
    
                .background(GeometryReader {
                    Color.clear.preference(key: ViewWidthKey.self,
                        value: $0.frame(in: .local).size.width)
                })
                .onPreferenceChange(ViewWidthKey.self) {
                    self.maxWidth = max($0, maxWidth)
                    width = max($0, maxWidth)
                }
            }
        }
        .pickerStyle(.wheel)
        .frame(maxWidth: width + 2 * 20 /* padding on both sides */)
    }
    

    Tested with Xcode 13.4 / iOS 15.5

    Test code on GitHub