I'm trying to create a Date Picker in Swiftui, but i need to do it in UIKit because I need the "minute Interval" thing, I created it, but I can not move around, is stuck in the same place on my screen. How can i make this work ? Is there a SwiftUI solution ?
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 = .time
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
}
}
}
var body: some View {
let minMaxRange = Date.now...Date.now
VStack {
Spacer()
DatePickerUIKit(selection: $dateFrom,
in: minMaxRange, minuteInterval: 30)
}
}
}
Here is a picture where is always located . 
CodePudding user response:
Based on "lorem ipsum" comment, I fixed it, finally.
let minMaxRange = Date.now...Date.now
DatePickerUIKit(selection: $dateFrom,in: minMaxRange, minuteInterval: 30)
.frame(width: 100, height: 100, alignment: .center)
CodePudding user response:
Default UIDatePicker does not have intrinsic content size which is used by SwiftUI UIViewRepresentable to determine frame of view.
Here is possible approach to improve your views so it fits own size (w/o hardcoding)
Tested with Xcode 13.2 / iOS 15.2 (red border is for testing, also removed spacer to show how date picker looks by itself)
struct DemoUIDatePickerView: View {
@State private var dateFrom: Date = Date()
var body: some View {
let minMaxRange = Date.now...Date.now
VStack {
DatePickerUIKit(selection: $dateFrom,
in: minMaxRange, minuteInterval: 30)
.border(Color.red) // << for testing !!!
}
}
}
extension UIDatePicker {
open override var intrinsicContentSize: CGSize { // << required !!
// calculates minimal needed size
sizeThatFits(.zero)
}
}
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
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 {
let datePicker = UIDatePicker()
datePicker.datePickerMode = .time
datePicker.minuteInterval = minuteInterval
datePicker.minimumDate = minimumDate
datePicker.maximumDate = maximumDate
// Next constrains are also required to avoid control hugging
datePicker.setContentHuggingPriority(.defaultHigh, for: .vertical) // << !!
datePicker.setContentHuggingPriority(.defaultHigh, for: .horizontal) // << !!
datePicker.addTarget(context.coordinator, action: #selector(Coordinator.changed(_:)), for: .valueChanged)
return datePicker
}
func updateUIView(_ datePicker: 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
}
}
}

