27

Working with UIDatePickers for the first time. To start, I'm just trying to update a text field with whatever value is selected from the datePicker (set in count down mode).

It works, but only the second time that a user selects a time. My action, durationSelected() is only called the second time, so there are no problems updating the textField.

Here's how I configured the date picker in my storyboard:

enter image description here

enter image description here

Here's the action triggered on Value Changed:

from DetailViewController.m

- (IBAction)durationSelected:(UIDatePicker *)sender
{
    self.durationTextField.text = [NSString stringWithFormat:@"%f seconds", sender.countDownDuration];
}

I've tried setting a default value, which had no effect.

What's going on?

Cœur
  • 37,241
  • 25
  • 195
  • 267
corbin
  • 2,797
  • 3
  • 18
  • 17
  • Can you define "the second time that a user selects a time"? Does that actually entail the user changing the date picker to a time other than the default time, and then changing it again? Or are you counting taps/touches as a time selection? I just want to point out that it is essential that the user is changing the time from the default value, considering the action event is value changed. – Stunner Oct 15 '13 at 03:02
  • on changing second time does it showing the first time or the second one? – kaustubh Oct 15 '13 at 06:54
  • looks like some reset of timer is required – Hussain Shabbir Oct 15 '13 at 12:27
  • Here's some clarification. When the user changes the time for the first time, nothing happens. When the user changes the time again, the Value Changed event is triggered, and the second time selected populates the text field. This happens regardless of whether a default value is set. Thanks! – corbin Oct 15 '13 at 18:45
  • @hussainShabbir can you elaborate? – corbin Oct 15 '13 at 18:45
  • Did you try to just log the countDownDuration on the method. Just to exclude that the problem is coming from the textfield. – cescofry Oct 16 '13 at 09:03

8 Answers8

24

I have seen a similar bug with the iOS 7.0.3 UIDatePicker when it is used in UIDatePickerModeCountDownTimer mode. The picker does not fire the target-action associated with the UIControlEventValueChanged event the first time the user changes the value by scrolling the wheels. It works fine for subsequent changes.

Below is an efficient workaround. Simply enclose the code that sets the initial value of the countDownDuration in a dispatch block to the main loop. Your target-action method will fire every time the wheels are rotated to a new value. This approach has almost no overhead and works quite well on an iPhone 4 and iPad 4.

dispatch_async(dispatch_get_main_queue(), ^{
    self.myDatePicker.countDownDuration = (NSTimeInterval) aNewDuration ;
});
Positron
  • 2,371
  • 1
  • 14
  • 8
16

This seems to be a bug in the iOS 7 implementation of UIDatePicker. I'd suggest filing a radar on it.

Building on the iOS 6 SDK and running on an iOS 6 device will work, while running on an iOS 7 device will not.

You can fix it by adding this line of code to your - (void)viewDidLoad method

[self.datePicker setDate:[NSDate date] animated:YES];
Evan
  • 750
  • 7
  • 13
  • @fivewood This still works for me in 9.3 using swift, can you provide an example project? – Evan Apr 14 '18 at 06:01
4

I'm not sure, it's an educated guess :

It might be that iOS is keeping track of value changes for the Date Picker only when they are caused by animating the wheel. So it sets the value on your first "roll" but only detects that has changed on the second.

As I said, I cannot be sure of the reason, but the fix should be simple: just set the staring date programmatically after the view loads using setDate: animated: :

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    [self.picker setDate:[NSDate dateWithTimeIntervalSinceNow:0] animated:true ];   
}

It seems to be working for me.

vinaut
  • 2,416
  • 15
  • 13
1

There is also a similar bug where it doesn't fire on the first change after you spin it to all 0's and it slides to the first minute.

Given both of these, I just use a brute force method to account for all this nonsense (assuming Apple is simply not interested in fixing this).

in viewDidLoad():

 NSTimer scheduledTimerWithTimeInterval:0.750 repeats:YES block:^(NSTimer * _Nonnull timer) {
     [self timePickerDidChange:nil];
 }];

Just keep calling the change event yourself. so long as it goes off of the current value (i.e. self.timePicker.countDownDuration then it works smoothly. Make sure your didChange code doesn't do anything big like a network call :)

IMFletcher
  • 656
  • 9
  • 19
0

You can try to set countDownTimer value (if you are using datePicker on countdowntimer mode). But make sure you set this property inside completion block after presenting the picker.

0

The following code after presenting the picker worked for me in Xcode 9.4 beta, iOS 11.4.

        picker.countDownDuration = 0
        picker.countDownDuration = 3600 // 1 Hour
fivewood
  • 391
  • 4
  • 11
0

I've had success with the following in Xcode 10, iOS 12.0.1 using Swift 4.0:

Using the other answers as a guide, I ended up making the UITextFields delegate respond to:

func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
  if let _ = textField.inputView as? UIDatePicker {
   //In my case I had more than one text field who's delegate would fire this method
   //So I checked to see if the input view was a date picker before continuing
   //If you have only one "delegated" text field then there's probably
   //no need for a check
   perform(#selector(setDatePicker), with: nil, afterDelay: 0)
  }
  return true
}

@objc func setDatePicker() {
  Thread.doBlockOnMainThread {
   self.theTimePicker.countDownDuration = 1000 //or whatever
  }
}

where Thread.doBlockOnMainThread(_) is:

extension Thread {
    class func doBlockOnMainThread(_ block: () -> Void) {
        if !Thread.isMainThread {
            DispatchQueue.main.sync(execute: {
                block()
            })
        } else {
            block()
        }
    }
}

As an aside, I found that it's not only when the UIDatePicker first appears but also when the user mistakenly tries to set the countdown value to zero. The DatePicker automatically moves to 1 as should be expected but then the very next value change will also not fire the requred target.

Todd
  • 1,770
  • 1
  • 17
  • 42
0

None of the above worked for me on iOS 13 / XCode 11.4.1. My solution was to use the "textFieldDidBeginEditing" delegate method for the textfield UITextFieldDelegate, as the datepicker needs to be in view and animated to simulate a "first" selection, so on second selection (perceived first selection to the user) the UIControlEventValueChanged method is triggered. So, something like this:

- (void)textFieldDidBeginEditing:(UITextField *)textField {

    [self.datePicker setDate: date]; //date is whatever date you want to set

}

On my method, on first run, I set the date variable to a minute interval of 5 using NSCalendar and NSDateComponents, as that's what I needed, and then after that I set date = self.datePicker.date.

This works except for the case where the user manually selects 0 minutes..

SMSidat
  • 1,163
  • 1
  • 15
  • 34