0

From https://stackoverflow.com/a/58664469/15445779 I have the DatePicker. I tried to edit it so, that I can change the months if the DatePicker is at the ends of the interval so, that only the months inside the interval gets displayed?

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 picker = UIPickerView(frame: .zero)

        picker.dataSource = context.coordinator
        picker.delegate = context.coordinator

        return picker
    }

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

    class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
        var parent: PickerView

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

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


My SwiftUI View: My idea is to change the data[0] Array to only the months that are inside of the interval. My question is how can I make that the picker displays only these months?

struct DatePicker_Interval:View{
    @State var data: [[String]]

    @State private var selections: [Int] = [0,0]
    
    @Environment(\.calendar) var calendar
    
    let interval: DateInterval
    
    init(interval: DateInterval) {
        
        self._data = State(initialValue: [
            Array(Calendar.current.component(.year, from: interval.start)...Calendar.current.component(.year,from: interval.end)).map { "\($0)" },
            Array(1...12).map { "\(DateFormatter().monthSymbols[$0-1])" },
        ])
        
        self.interval = interval
    }
    
    var body: some View{
        VStack{
            PickerView(data: data, selections: $selections)
                .onChange(of: selections[0], perform: { _ in
                    if(data[0][selections[0]] == String(Calendar.current.component(.year, from: interval.start))){
                        print(interval.start)
                        print(calendar.component(.month, from: interval.start))
                        print(Array(calendar.component(.month, from: interval.start)...12).map { "\(DateFormatter().monthSymbols[$0-1])" })
                        data[1] = Array(calendar.component(.month, from: interval.start)...12).map { "\(DateFormatter().monthSymbols[$0-1])" }
                    }else if(data[0][selections[0]] == String(Calendar.current.component(.year, from: interval.end))){
                        print(interval.end)
                        print(calendar.component(.month, from: interval.end))
                        print(Array(1...calendar.component(.month, from: interval.end)).map { "\(DateFormatter().monthSymbols[$0-1])" })
                        data[1] = Array(1...calendar.component(.month, from: interval.end)).map { "\(DateFormatter().monthSymbols[$0-1])" }
                    }
                })
            HStack{
                Text("selection[0]: \(selections[0])")
                Text("selection[1]: \(selections[1])")
            }
            Text("Intervall: Start - \(interval.start.description)")
            Text("Intervall: End - \(interval.end.description)")
        }
    }
}

Content View


struct ContentView: View {
    @Environment(\.calendar) var calendar
    
    private var testInterval: DateInterval {
        let comp = DateComponents(year: 2001, month: 5)
        let comp2 = DateComponents(year: 2002, month: 8)
        return DateInterval(start: calendar.date(from: comp) ?? Date(), end: calendar.date(from: comp2) ?? Date())
    }
    var body: some View {
        DatePicker_Interval(interval: testInterval)
    }
}

malhal
  • 26,330
  • 7
  • 115
  • 133
sheldor
  • 115
  • 12
  • Where did you get that `Coordinator(self)` major mistake from? I see it a lot – malhal May 16 '23 at 07:54
  • What do you mean exactly? The Picker is from https://stackoverflow.com/a/58664469/15445779 – sheldor May 16 '23 at 20:00
  • You can't do `Coordinator(self)` because `self` is the struct which will be out of date when it is recreated by the paren't View's body. Thus anything that uses it in the coordinator will be using the wrong version of the struct. – malhal May 16 '23 at 22:11
  • I'll add a fix but its not as good as passing in just the binding. – malhal May 16 '23 at 22:20
  • I added also `view.dataSource = context.coordinator` `view.delegate = context.coordinator` under your line. Then the picker changed the appearance. Do you mean a binding of the `data` variable? – sheldor May 18 '23 at 14:58
  • Yes like this, `context.coordinator.selectionsBinding = _selections` – malhal May 18 '23 at 16:41
  • And in the coordinator have `var selectionsBinding: Binding<[Int]>?` and `selectionsBinding?.wrappedValue = ...` – malhal May 18 '23 at 16:52

0 Answers0