leonboe1
leonboe1

Reputation: 1185

How to change UIPickerView width with multiple Pickers in SwiftUI?

I need to use an UIPickerView instead of the SwiftUI Picker since there are some bugs with it and I need more control over it.

Everything works fine with the approach below, however, I need a picker which has a smaller width (something around 70% of the original width) but still shows all three pickers and still has the rounded corners of the gray background (so the gray background frame width and the spacing between the three pickers should decrease).

I tried to modify the frame of the picker and its superview but that didn't work at all. Setting the frame width in SwiftUI and then using the clipped modifier cuts off the rounded edges and also cuts off some parts of the numbers (so that's out of the question).

Does somebody know how to do this? Thanks a lot!

enter image description here

import SwiftUI

struct ContentView: View {

    @State private var selections: [Int] = [5, 10, 50]

    var body: some View {
        MainPicker(pickerSelections: self.$selections)
    }
}

struct MainPicker: View {
    
    @Binding var pickerSelections: [Int]
    
    private let data: [[String]] = [
        Array(0...59).map { "\($0 < 10 ? "0" : "")" + "\($0)" },
        Array(0...59).map { "\($0 < 10 ? "0" : "")" + "\($0)" },
        Array(0...59).map { "\($0 < 10 ? "0" : "")" + "\($0)" }
    ]
    
    var body: some View {
        HStack{
            PickerView(data: data, selections: self.$pickerSelections)
        }
    }
}

struct PickerView: UIViewRepresentable {
    var data: [[String]]
    @Binding var selections: [Int]

    //makeCoordinator()
    func makeCoordinator() -> PickerView.Coordinator {
        Coordinator(self)
    }

    //makeUIView(context:)
    func makeUIView(context: UIViewRepresentableContext<PickerView>) -> UIPickerView {

        let hoursLabel = UILabel()
        let minLabel = UILabel()
        let secLabel = UILabel()
        hoursLabel.text = "h"
        minLabel.text = "m"
        secLabel.text = "s"
        
        let picker = UIPickerView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) //doesnt work
        
        
        picker.dataSource = context.coordinator
        picker.delegate = context.coordinator
        
        picker.superview?.frame = CGRect(x: 0, y: 0, width: 100, height: 100) //doesnt work
        
        return picker
    }

    //updateUIView(_:context:)
    func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<PickerView>) {
        for i in 0...(self.selections.count - 1) {
            if(context.coordinator.initialSelection[i] != self.selections[i]){
                view.selectRow(self.selections[i], inComponent: i, animated: false)
                context.coordinator.initialSelection[i] = self.selections[i]
            }
        }
    }

    class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
        var parent: PickerView
        var initialSelection = [-1, -1, -1]
        
        //init(_:)
        init(_ pickerView: PickerView) {
            
            self.parent = pickerView
        }

        //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
        }
        
        func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
            return 50
        }

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

Upvotes: 3

Views: 2605

Answers (1)

Asperi
Asperi

Reputation: 257533

You need to remove compression resistance priority

let picker = UIPickerView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
picker.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

and then can use any frame as you need in SwiftUI part (static or calculated)

demo

    MainPicker(pickerSelections: self.$selections)
        .frame(width: 200)

Demo prepared & tested with Xcode 11.7 / iOS 13.7

Upvotes: 5

Related Questions