I am using a UIDatePicker
in my app to allow picking time:
let startTimePicker = UIDatePicker()
startTimePicker.addTarget.preferredDatePickerStyle = .compact
startTimePicker.addTarget.datePickerMode = .time
However, I only want user to be able to pick this time after they have entered a password correctly. For incorrect password, I want the tap on the picker to be cancelled.
I am able to detect when tap occurs by using editingDidBegin
:
startTimePicker.addTarget(self, action: #selector(beginChangingTimes(sender:)), for: .editingDidBegin)
However, I am unable to cancel the tap or at least prevent it from showing the picker until correct password has been entered.
In my beginChangingTimes(sender:)
function, I have tried things like setting the picker isEnabled
to false then true, setting isUserInteractionEnabled
to false then true, calling resignFirstResponder()
on the picker, and calling endEditing(true)
on the picker. None of these seem to cancel the tap and it just keeps showing the actual picker UI:
How can I cancel it?
You cannot cancel a tap after it begins with .compact
style UIDatePicker
, but you can:
Prevent the tap using an overlay (UIButton
) that intercepts touch.
Or, use a UITextField
+ .inputView
for better control via delegate.
Avoid relying on .editingDidBegin
or toggling isEnabled
dynamically. It’s too late.
Instead of trying to cancel the event after it starts, you can block the interaction altogether unless the password is verified.
UIDatePicker
in a Custom Container with a Transparent Button Overlaylet containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
let datePicker = UIDatePicker()
datePicker.preferredDatePickerStyle = .compact
datePicker.datePickerMode = .time
datePicker.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(datePicker)
// Overlay button to block interaction
let blockerButton = UIButton()
blockerButton.translatesAutoresizingMaskIntoConstraints = false
blockerButton.backgroundColor = .clear
blockerButton.addTarget(self, action: #selector(promptForPassword), for: .touchUpInside)
containerView.addSubview(blockerButton)
// Constraints
NSLayoutConstraint.activate([
datePicker.topAnchor.constraint(equalTo: containerView.topAnchor),
datePicker.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
datePicker.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
datePicker.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
blockerButton.topAnchor.constraint(equalTo: containerView.topAnchor),
blockerButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
blockerButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
blockerButton.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
])
Then:
@objc func promptForPassword() {
// Ask for password here
// If valid, remove blocker
if isPasswordCorrect {
blockerButton.removeFromSuperview()
} else {
showPasswordError()
}
}
UITextField
+ UIDatePicker
as inputView
(Full Control)let timeField = UITextField()
timeField.placeholder = "Select Time"
timeField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(timeField)
let picker = UIDatePicker()
picker.datePickerMode = .time
picker.preferredDatePickerStyle = .wheels
picker.addTarget(self, action: #selector(dateChanged(_:)), for: .valueChanged)
timeField.inputView = picker
// Password check before editing
timeField.delegate = self
And conform to:
extension YourViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
if !isPasswordCorrect {
showPasswordError()
return false
}
return true
}
}