I would like to create a UIViewRepresentable
holding a UITextField
using an UIDatePicker
as inputView
While the code below works fine then the DatePicker
is directly used as inputView
, it does not work anymore, when the DatePicker
is embedded in some containerView
(required to add additional controls).
Neither adding a size constraint to the DatePicker
or the containerView
, nor using a ContainerView with intrinsic size, changed the result.
Any idea what is wrong and how to correctly size the DatePicker?
struct DatePickerTextField: UIViewRepresentable {
private let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd.MM.yyyy"
return dateFormatter
}()
public var placeholder: String
@Binding public var date: Date?
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
let datePicker = UIDatePicker()
let container = DatePickerContainer()
datePicker.translatesAutoresizingMaskIntoConstraints = false
container.addSubview(datePicker)
NSLayoutConstraint.activate([
datePicker.leadingAnchor.constraint(equalTo: container.leadingAnchor),
datePicker.trailingAnchor.constraint(equalTo: container.trailingAnchor),
datePicker.topAnchor.constraint(equalTo: container.topAnchor),
datePicker.bottomAnchor.constraint(equalTo: container.bottomAnchor),
//datePicker.heightAnchor.constraint(equalToConstant: 300)
])
datePicker.datePickerMode = .date
datePicker.preferredDatePickerStyle = .inline
//textField.inputView = datePicker // correct height
textField.inputView = container
// Accessory View
let toolbar = UIToolbar()
toolbar.sizeToFit()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done",
style: .plain,
target: context.coordinator,
action: #selector(context.coordinator.doneButtonAction))
toolbar.setItems([flexibleSpace, doneButton], animated: true)
textField.inputAccessoryView = toolbar
context.coordinator.dateChanged = {
self.date = datePicker.date
}
context.coordinator.doneButtonTapped = {
if self.date == nil {
self.date = datePicker.date
}
textField.resignFirstResponder()
}
return textField
}
class DatePickerContainer: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: UIView.noIntrinsicMetric, height: 300)
}
}
func updateUIView(_ uiView: UITextField, context: Context) {
if let selectedDate = self.date {
uiView.text = self.dateFormatter.string(from: selectedDate)
}
}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator {
public var dateChanged: (() -> Void)?
public var doneButtonTapped: (() -> Void)?
@objc func dateValueChanged() {
self.dateChanged?()
}
@objc func doneButtonAction() {
self.doneButtonTapped?()
}
}
}
All you need to do is to set bounds.size.height
to the desired height, and this will become the height of the keyboard. You don't need to override intrinsicContentSize
at all.
let container = DatePickerContainer()
container.bounds.size.height = 300
// everything else stays the same
Or if you would like to use AutoLayout, you need to disable translatesAutoresizingMaskIntoConstraints
on container
as well.
container.translatesAutoresizingMaskIntoConstraints = false
// now these constraints will resize the container to 300px tall
NSLayoutConstraint.activate([
datePicker.leadingAnchor.constraint(equalTo: container.leadingAnchor),
datePicker.trailingAnchor.constraint(equalTo: container.trailingAnchor),
datePicker.topAnchor.constraint(equalTo: container.topAnchor),
datePicker.bottomAnchor.constraint(equalTo: container.bottomAnchor),
datePicker.heightAnchor.constraint(equalToConstant: 300)
])