SwiftUI:如何创建自定义 UIDatePicker?

SwiftUI: how to create custom UIDatePicker?

我成功地在 SwifUI 中创建了 UIDatePicker,但是当我在选择器中更改日期轮时,日期值没有更新,这是我的代码:

struct DatePicker: UIViewRepresentable {
    @Binding var date: Date
    
    func makeUIView(context: Context) -> UIDatePicker {
        let view = UIDatePicker()
        view.datePickerMode = .date
        return view
    }
    func updateUIView(_ uiView: UIDatePicker, context: Context) {
        uiView.date = date
    }
}

然后我从视图中这样调用它:

struct SwiftUIView: View {
  @State var date = Date()
    var body: some View {
      DatePicker(date: self.$date)
  }
}

这是可能的方法。使用 Xcode 11.2 / iOS 13.2

测试
struct DatePicker: UIViewRepresentable {
    @Binding var date: Date

    func makeUIView(context: Context) -> UIDatePicker {
        let datePicker = UIDatePicker()
        datePicker.datePickerMode = .date
        datePicker.addTarget(context.coordinator, action: #selector(Coordinator.changed(_:)), for: .valueChanged)
        return datePicker
    }

    func updateUIView(_ datePicker: UIDatePicker, context: Context) {
        datePicker.date = date
    }

    func makeCoordinator() -> DatePicker.Coordinator {
        Coordinator(date: $date)
    }

    class Coordinator: NSObject {
        private let date: Binding<Date>

        init(date: Binding<Date>) {
            self.date = date
        }

        @objc func changed(_ sender: UIDatePicker) {
            self.date.wrappedValue = sender.date
        }
    }
}

backup

带有可选参数的扩展 DatePicker 方法(基于@Asperi 回答)。

struct DatePickerUIKit: UIViewRepresentable {
    @Binding private var selection: Date

    private let range: ClosedRange<Date>?

    private var minimumDate: Date? {
         range?.lowerBound
    }

    private var maximumDate: Date? {
        range?.upperBound
    }

    private var minuteInterval: Int

    private let datePicker = UIDatePicker()

    init(selection: Binding<Date>, in range: ClosedRange<Date>?, minuteInterval: Int = 1) {
        self._selection = selection
        self.range = range
        self.minuteInterval = minuteInterval
    }

    func makeUIView(context: Context) -> UIDatePicker {
        datePicker.datePickerMode = .dateAndTime
        datePicker.minuteInterval = minuteInterval
        datePicker.minimumDate = minimumDate
        datePicker.maximumDate = maximumDate
        datePicker.addTarget(context.coordinator, action: #selector(Coordinator.changed(_:)), for: .valueChanged)
        return datePicker
    }

    func updateUIView(_ uiView: UIDatePicker, context: Context) {
        datePicker.date = selection

    }

    func makeCoordinator() -> DatePickerUIKit.Coordinator {
        Coordinator(selection: $selection, in: range, minuteInterval: minuteInterval)
    }

    class Coordinator: NSObject {
        private let selection: Binding<Date>
        private let range: ClosedRange<Date>?
        private let minuteInterval: Int

        init(selection: Binding<Date>, in range: ClosedRange<Date>? = nil, minuteInterval: Int = 1) {
            self.selection = selection
            self.range = range
            self.minuteInterval = minuteInterval
        }

        @objc func changed(_ sender: UIDatePicker) {
            self.selection.wrappedValue = sender.date
        }
    }
}

例如设置分钟间隔SwiftUI尚不支持:

@State var dateFrom = Date()

let minMaxRange = Date().dateAtStartOf(.day)...Date().dateAtEndOf(.day) + 3.days

DatePickerUIKit(selection: $dateFrom, 
             range: minMaxRange, minuteInterval: 30)