'UIDate Picker valuechanged does not update the first spin, but does every spin after.. iOS

I have a UIDate Picker embedded in a static TableViewCell and at the moment I disabled most of the code except the code responsible for the date picker.

I'm using the Date Picker as a Count Down Timer

So this is kind of all, except some usual outlets and vars:

override func viewDidLoad() {
    super.viewDidLoad()

    //  tfName.delegate = self
    //  tfDescription.delegate = self
    //

    datePicker.countDownDuration = 60

    //  pickerValueChanged(self)

}


@IBAction func pickerValueChanged(sender: AnyObject) {

     seconds  = Int(datePicker.countDownDuration)
     hours  = seconds / 3600
    if hours == 0{
        minutes = seconds / 60
    } else{
        minutes = seconds % 3600 / 60
    }
    labelDuration.text = "\(hours) hours \(minutes) minutes"
}

The first time I change the value of the Picker, the value changed function does not fire at all, but the spins after it does without failing.

I tried to call it in viewDidLoad, with pickerValueChanged(self) and that works, but does not solve the problem. Also I commented out every line of code in the pickerValueChanged function and viewDidLoad but it still did not fire the first spin..

Thank you for your time!

Update: Added pictures

After the first spin, pickerValueChanged does not get called: First spin

From the second spin and beyond, the event gets called and the label is updated: Second spin

Tried:

Executing: datePicker.setDate(NSDate(), animated: true in viewDidLoad() solves the problem that the event doesn't get called at the first spin, but this sets the initial state of the Date Picker to the current time. Since I'm using this control to set a duration to a event, I want to let it start at 0 hour and 1 minute. This can be done with datePicker.countDownDuration = 60 but after this, the main problem gets back again. So a solution could be setting the DatePicker with a NSDate of 1 minute, but how to do this?

Solution:

var dateComp : NSDateComponents = NSDateComponents()
dateComp.hour = 0
dateComp.minute = 1
dateComp.timeZone = NSTimeZone.systemTimeZone()
var calendar : NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)!
var date : NSDate = calendar.dateFromComponents(dateComp)!

datePicker.setDate(date, animated: true)


Solution 1:[1]

I came up the following solution to initialize the datePicker component (in countdown timer mode) with 0 hours and 1 minute and it responds directly at the first spin. Since this appears to be a bug and is very frustrating if you want a textlabel to update its value when the datePicker is changed and it does not at the first go:

var dateComp : NSDateComponents = NSDateComponents()
dateComp.hour = 0
dateComp.minute = 1
dateComp.timeZone = NSTimeZone.systemTimeZone()
var calendar : NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)!
var date : NSDate = calendar.dateFromComponents(dateComp)!

datePicker.setDate(date, animated: true)

The timeZone component is not really needed for this to work for my implementation.

Solution 2:[2]

It seems to be something to do with the animated parameter. You just need this line:

datePicker.setDate(date, animated: true)

Solution 3:[3]

Time ago I found another problem that somehow resembles this one. It was about presenting a view controller from within the tableView:didSelectRowAtIndexPath: and the problem was that it was taking lots of time for the new controller to actually show up. One of the workarounds turned out to be using dispatch_async on the main queue. The same worked for me in this case.

In my viewDidLoad, I asynchronously dispatched the picker setup code on the main queue and, in my case, it started to respond to the valueChanged event even on the first pick:

DispatchQueue.main.async {
    self.pickerView.datePickerMode = .countDownTimer
    self.pickerView.minuteInterval = 5
    self.pickerView.countDownDuration = 15
}

Solution 4:[4]

Here's @Henk-Martijn's solution updated & simplified for Swift 3:

let calendar = Calendar(identifier: .gregorian)
let date = DateComponents(calendar: calendar, hour: 1, minute: 30).date!
datePicker.setDate(date, animated: true)

Use the above code instead of something like:

datePicker.countDownDuration = 5_400

Solution 5:[5]

Similar problem as above, I used

DispatchQueue.main.asyncAfter(deadline: .now()) {
   self.datePicker.countDownDuration = 60
}

to put it on the next runloop. Seems a viable workaround.

Solution 6:[6]

I think you should try implement this delegate instead

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

Solution 7:[7]

Actually, all you need is to not set datePicker.countDownDuration in viewDidLoad but add it to viewDidAppear, or later.

Solution 8:[8]

I don't know if I got your problem, but Date picker works using the target-action pattern and it triggers this mechanism when you change a value.
So if the problem is the very first value shown, you should initialize your variable taking it as the "default" value.

Solution 9:[9]

None of these solutions work, when you use the DatePicker as an inputView for a textfield.

I have attached my code below. I am still searching for a good solution for this, because none of the above fixes the problem in my scenario.

self.durationDatePicker.datePickerMode = .countDownTimer
self.durationDatePicker.preferredDatePickerStyle = .wheels
self.durationDatePicker.minuteInterval = 5
self.durationDatePicker.addTarget(self, action: #selector(CreateEventDetailsViewController.datePickerValueChanged(picker:)), for: .valueChanged)
self.durationTextField.inputView = self.durationDatePicker

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Henk-Martijn
Solution 2 Mathias Claassen
Solution 3 Marcin Czenko
Solution 4 Jeehut
Solution 5 JeffL
Solution 6 dminones
Solution 7 Pranav Kasetti
Solution 8 Andrea
Solution 9 Mikkel Cortnum