21

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)
Henk-Martijn
  • 2,024
  • 21
  • 25

9 Answers9

6

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.

Henk-Martijn
  • 2,024
  • 21
  • 25
  • This works until the DatePicker does not disappear (for instance if I have table cell which reveals or hides it). If I open the cell where the DatePicker is, the problem is here again and even setting the Date of the DatePicker does not help. This is pretty crazy.... – Dareon Oct 19 '16 at 17:50
3

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

datePicker.setDate(date, animated: true)
Mathias Claassen
  • 557
  • 4
  • 10
  • This seemed to be the problem for me. I used to set the date with `animated: false`, changing this to `true` made my target action being called also on the first time changing the value. – Marijn Sep 20 '16 at 08:21
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
}
Marcin Czenko
  • 401
  • 3
  • 11
3

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
Jeehut
  • 20,202
  • 8
  • 59
  • 80
3

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.

JeffL
  • 151
  • 1
  • 6
1

I think you should try implement this delegate instead

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
dminones
  • 2,236
  • 20
  • 21
  • I tried setting a delegate for the Date Picker before, but this control does not have a delegate as the PickerView does have, as Andrea also explains – Henk-Martijn Feb 03 '15 at 11:29
  • 1
    Oh, i see. Did you see this post:http://stackoverflow.com/questions/19251803/objective-c-uidatepicker-uicontroleventvaluechanged-only-fired-on-second-select – dminones Feb 03 '15 at 13:54
  • It seems like the bug they talk about is still there. setting datePicker.setDate(NSDate(), animated: true) in viewDidLoad solves the problem partly. The DatePicker now updates the value with the first spin now to, but it syncs the hours & minutes with the local time. And I want it to start at 1 minute, which I did with datePicker.countDownDuration = 60, but this breaks the function again :D. So I need to init the picker with a NSDate constant with (0h), 1minute. But I have no idea how to archieve this – Henk-Martijn Feb 03 '15 at 15:01
1

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

Pranav Kasetti
  • 8,770
  • 2
  • 50
  • 71
sebby
  • 21
  • 2
0

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.

Andrea
  • 26,120
  • 10
  • 85
  • 131
  • 1
    What happens is, the first time I give the Date Picker a *swing" it spins to some value, but does not trigger the Value Changed event at all. But the second swing and all after does trigger the event. Even triggering the event from viewDidLoad does not change the behavior of the problem. – Henk-Martijn Feb 03 '15 at 11:35
0

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
Mikkel Cortnum
  • 481
  • 4
  • 11