Close UIdatepicker After Selection When Style Is .Compact

Close UIDatePicker after selection when style is .compact

For now, you can do:

presentedViewController?.dismiss(animated: true, completion: nil)

i.e.

override func viewDidLoad() {
super.viewDidLoad()
datePicker.addTarget(self, action: #selector(dateChanged), for: .valueChanged)
}

@objc private func dateChanged() {
presentedViewController?.dismiss(animated: true, completion: nil)
}

But note that this will potentially break in future iOS versions as this relies on the implementation detail that compact date pickers presents a view controller to show the calendar. If you print out the type of the presented VC, you'll see _UIDatePickerIOSCompactViewController. The leading _ suggests that this is an implementation detail.

All that is documented about compact is:

A style indicating that the date picker displays as a label that when tapped displays a calendar-style editor.

So in future versions of iOS, they could instead change the implementation to not present a new view controller, and this could very well break, and you'll have to find another way then. I see no documented way of doing this right now.

How to close a compact UIDatePicker popup when choosing a date?

If you do want to implement it you need to create a custom Picker .

in UIViewController

    UITextField textField;
public override void ViewDidLoad ()
{
base.ViewDidLoad ();

textField = new UITextField(new CGRect(10, 100, 200, 80));

textField.TextColor = UIColor.Red;
textField.BackgroundColor = UIColor.LightGray; // you could set the style as you want
// textField.Delegate = new MyPickerDelegate();

UIDatePicker datePicker = new UIDatePicker();
datePicker.BackgroundColor = UIColor.LightGray;
datePicker.ValueChanged += DatePicker_ValueChanged;

if(UIDevice.CurrentDevice.CheckSystemVersion(14,0))
{
datePicker.PreferredDatePickerStyle = UIDatePickerStyle.Inline;
}

else
{
datePicker.PreferredDatePickerStyle = UIDatePickerStyle.Wheels;
}

textField.InputView = datePicker;

View.AddSubview(textField);
// Perform any additional setup after loading the view, typically from a nib.
}

private void DatePicker_ValueChanged(object sender, EventArgs e)
{
var picker = sender as UIDatePicker;

var date = picker.Date;

var dateFormatter =new NSDateFormatter();

dateFormatter.DateFormat = "YYYY-MM-dd HH:mm";

var dateString = dateFormatter.ToString(date);
textField.Text = dateString;
textField.ResignFirstResponder();

}

public override void TouchesBegan(NSSet touches, UIEvent evt)
{
base.TouchesBegan(touches, evt);

textField.ResignFirstResponder();

}

UIDatePicker UIDatePickerStyle = .compact notification when pop-up calendar is dismissed

You can try to observe editingDidEnd control event:

yourDatePicker.addTarget(self, action: #selector(yourCallback), for: .editingDidEnd)

How to reset inline iOS UIDatePicker when no date is selected

That's curious...

My guess would be that when using the older "wheel" date picker style, a new date is selected when the wheels stop scrolling... there is no way to "scroll away" from the selected date.

My second guess would be that this issue could be changed ("fixed") in a future iOS update.

Anyway, here is one work-around...

  • get today's date
  • get the selected date from the picker
  • if they are the same, user may have scrolled the months / years, so
    • add one day to "today"
    • set the picker to "tomorrow" (animated)
    • make an async call to set the picker to "today"
  • if the dates are not the same, just set the picker to "today" (animated)

I've only done a quick test of this, so you'll want to thoroughly test it. And probably add some additional checking -- such as what happens if the calendar rolls over midnight while the picker is displayed; how does it look if "today" is the last day of the month; etc.

    // get "today" date
let today = Date()

// get selected date
let pickerDate = self.datePicker.date

// are the dates the same day?
let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)

if todayIsSelected {
// picker has today selected, but may have scrolled months...

// should never fail, but this unwraps the optional
guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
return
}

// animate to "tomorrow"
self.datePicker.setDate(nextDay, animated: true)

// async call to animate to "today"
DispatchQueue.main.async {
self.datePicker.setDate(today, animated: true)
}
} else {
// picker has a different date selected
// so just animate to "today"
self.datePicker.setDate(today, animated: true)
}


Edit - complete example:

class ScratchVC: UIViewController {

let datePicker = UIDatePicker()
let btn = UIButton()

override func viewDidLoad() {
super.viewDidLoad()

if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
// Fallback on earlier versions
}

btn.backgroundColor = .red
btn.setTitle("Go To Today", for: [])
btn.setTitleColor(.white, for: .normal)
btn.setTitleColor(.gray, for: .highlighted)

btn.translatesAutoresizingMaskIntoConstraints = false
datePicker.translatesAutoresizingMaskIntoConstraints = false

view.addSubview(btn)
view.addSubview(datePicker)

let g = view.safeAreaLayoutGuide

NSLayoutConstraint.activate([

btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),

datePicker.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.9),
datePicker.centerXAnchor.constraint(equalTo: g.centerXAnchor),
datePicker.centerYAnchor.constraint(equalTo: g.centerYAnchor),

])

btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)

}

@objc func didTap(_ sender: Any) {

// get "today" date
let today = Date()

// get selected date
let pickerDate = self.datePicker.date

// are the dates the same day?
let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)

if todayIsSelected {
// picker has today selected, but may have scrolled months...

// should never fail, but this unwraps the optional
guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
return
}

// animate to "tomorrow"
self.datePicker.setDate(nextDay, animated: true)

// async call to animate to "today" - delay for 0.1 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
self.datePicker.setDate(today, animated: true)
})
} else {
// picker has a different date selected
// so just animate to "today"
self.datePicker.setDate(today, animated: true)
}

}

}

Anyone find that UIDatePicker is broken under iOS 14?

To add some additional information to Oscar's answer...

UIDatePicker now has three different styles, as per documentation here and here. The date picker's style is accessible by the .preferredDatePickerStyle property which can be set to four different cases:

  1. .automatic - A style indicating that the system picks the concrete style based on the current platform and date picker mode.
  2. .compact - A style indicating that the date picker displays as a label that when tapped displays a calendar-style editor.
  3. .inline - A style indicating that the date pickers displays as an inline, editable field.
  4. .wheels - A style indicating that the date picker displays as a wheel picker.

If you want things to look how they did pre iOS 14, stick with the .wheels style. It is also worth noting that not all styles can accommodate all date and time settings, hence why you can only set a preferred style.

Lastly, this property is only available in iOS 13.4 or newer, so you will have to accomplish version control with something like:

if #available(iOS 13.4, *) {
yourDatePicker.preferredDatePickerStyle = UIDatePickerStyle.automatic
} else {
// Fallback on earlier versions
}

UIDatePicker pop up after UIButton is pressed

canihazcode?

Yes, sir. Thanks for helping me procrastinating.

- (void)changeDate:(UIDatePicker *)sender {
NSLog(@"New Date: %@", sender.date);
}

- (void)removeViews:(id)object {
[[self.view viewWithTag:9] removeFromSuperview];
[[self.view viewWithTag:10] removeFromSuperview];
[[self.view viewWithTag:11] removeFromSuperview];
}

- (void)dismissDatePicker:(id)sender {
CGRect toolbarTargetFrame = CGRectMake(0, self.view.bounds.size.height, 320, 44);
CGRect datePickerTargetFrame = CGRectMake(0, self.view.bounds.size.height+44, 320, 216);
[UIView beginAnimations:@"MoveOut" context:nil];
[self.view viewWithTag:9].alpha = 0;
[self.view viewWithTag:10].frame = datePickerTargetFrame;
[self.view viewWithTag:11].frame = toolbarTargetFrame;
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(removeViews:)];
[UIView commitAnimations];
}

- (IBAction)callDP:(id)sender {
if ([self.view viewWithTag:9]) {
return;
}
CGRect toolbarTargetFrame = CGRectMake(0, self.view.bounds.size.height-216-44, 320, 44);
CGRect datePickerTargetFrame = CGRectMake(0, self.view.bounds.size.height-216, 320, 216);

UIView *darkView = [[[UIView alloc] initWithFrame:self.view.bounds] autorelease];
darkView.alpha = 0;
darkView.backgroundColor = [UIColor blackColor];
darkView.tag = 9;
UITapGestureRecognizer *tapGesture = [[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissDatePicker:)] autorelease];
[darkView addGestureRecognizer:tapGesture];
[self.view addSubview:darkView];

UIDatePicker *datePicker = [[[UIDatePicker alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height+44, 320, 216)] autorelease];
datePicker.tag = 10;
[datePicker addTarget:self action:@selector(changeDate:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:datePicker];

UIToolbar *toolBar = [[[UIToolbar alloc] initWithFrame:CGRectMake(0, self.view.bounds.size.height, 320, 44)] autorelease];
toolBar.tag = 11;
toolBar.barStyle = UIBarStyleBlackTranslucent;
UIBarButtonItem *spacer = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil] autorelease];
UIBarButtonItem *doneButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismissDatePicker:)] autorelease];
[toolBar setItems:[NSArray arrayWithObjects:spacer, doneButton, nil]];
[self.view addSubview:toolBar];

[UIView beginAnimations:@"MoveIn" context:nil];
toolBar.frame = toolbarTargetFrame;
datePicker.frame = datePickerTargetFrame;
darkView.alpha = 0.5;
[UIView commitAnimations];
}


Related Topics



Leave a reply



Submit