layoutswiftuiuipickerviewspacing

SwiftUI: Setting Picker line height


For large fonts, the lines in a Picker are overlapping. How do I change the Picker's line height? (Hint: the .lineSpacing modifier doesn't do it.)

See Also

This question is similar to that of Ejaaz, but his is so far unanswered.

The Problem

picker view

The Code

The following runnable code yields the above result. I don't really want different-sized lines, I just want the large fonts to fit properly. I have tried inserting Spacers, adding .frame modifiers here and there, .lineSpacing, padding() ... maybe just haven't found the right combination?

struct ContentView: View {
    @State private var selected = 0

    var body: some View {
        Picker(selection: self.$selected, label: Text("Letters")) {
            Text("A").font(.system(size: 30))
            Text("B").font(.system(size: 40))
            Text("C").font(.system(size: 50))
            Text("D").font(.system(size: 60))
            Text("E").font(.system(size: 70))
            Text("F").font(.system(size: 80))
        }
    }
}

Solution

  • enter image description here

    You have to wrap UIPickerView in a UIViewRepresentable and then use:

     func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat 
    

    … to change your row height.

    Example

    It just shows how to change row height, you still have to extend it for your contents...

    struct PickerView: UIViewRepresentable {
        var data: [[String]]
        @Binding var selections: Int
    
        // Assign custom coordinator for delegate functions.
        func makeCoordinator() -> PickerView.Coordinator {
            Coordinator(self)
        }
    
        // Creates the view object and configures its initial state.
        func makeUIView(context: UIViewRepresentableContext<PickerView>) -> UIPickerView {
            let picker = UIPickerView(frame: .zero)
    
            picker.dataSource = context.coordinator
            picker.delegate = context.coordinator
    
            return picker
        }
    
        // Updates the state of the specified view with new information from SwiftUI.
        func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<PickerView>) {
            // for i in 0...(self.selections.count - 1) {
            //     view.selectRow(self.selections[i], inComponent: i, animated: false)
            // }
        }
    
    
        // Coordinator acting as the delegate in SwiftUI.
        class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
            var parent: PickerView
    
            //init(_:)
            init(_ pickerView: PickerView) {
                self.parent = pickerView
            }
            
            // Set your custom row height here!
            func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
                return 80
            }
    
            //numberOfComponents(in:)
            func numberOfComponents(in pickerView: UIPickerView) -> Int {
                return self.parent.data.count
            }
    
            //pickerView(_:numberOfRowsInComponent:)
            func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
                return self.parent.data[component].count
            }
    
            //pickerView(_:titleForRow:forComponent:)
            func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
                return self.parent.data[component][row]
            }
    
            //pickerView(_:didSelectRow:inComponent:)
            func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
                // self.parent.selections[component] = row
            }
        }
    }
    

    To use it in your SwiftUI view, simply place the custom PickerView() in your body.