UIDatePicker bug? UIControlEventValueChanged after hitting minimum internal
Asked Answered
S

7

28

I've run into a weird effect that sure looks like a bug in iOS7 -- but often in the past, when I have thought I found a bug in Apple's APIs, it has turned out to be my own misunderstanding.

I have a UIDatePicker with datePickerMode = UIDatePickerModeCountDownTimer and minuteInterval = 5. I initialize the duration to 1 hour, and present it to the user, where it appears as a two-column picker with hours and minutes. (So far so good.)

The user is thinking "20 minutes," and so scrolls the Hour column to 0. At this point the picker reads 0 hours and 0 minutes, and iOS7 is not cool with that, so it automatically scrolls the minute wheel to 5. My UIControlEventValueChanged handler gets invoked, and the countDownDuration reads 5 minutes. (Still good.)

Now the user grabs the minute wheel and drags it to 20. AND... my UIControlEventValueChanged handler does not get called. (Bad.)

If I have some other event in the UI check the date picker at this point, I do see the countDownDuration is set to 20. But I had no way of knowing that the user changed it, at the moment it was changed. This is very repeatable: it always happens on the first change AFTER the picker refuses to be set to 0 (advancing itself to 5 minutes).

Note that this is in iOS7; it does not occur in iOS6 (perhaps because the picker there is perfectly content to be set to 0 minutes).

So... am I missing something here? Or is this a genuine bug in iOS7? And in the latter case, does anybody know a work-around better than having some timer periodically check the current interval?

Skylight answered 24/11, 2013 at 22:33 Comment(3)
Reproduced. Looks like a bug to me.Ichthyic
I'm still seeing this issue in iOS 12.1. From users' comments this bug also appeared in at least iOS 7.1, 8, 8.1, and 10.Rrhagia
Issue still exists in iOS 13...Adventure
C
35

I can also confirm that the iOS 7.0.3 UIDatePicker has a bug in it when 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 ;
});

Swift 5:

DispatchQueue.main.async {
    self.myDatePicker.countDownDuration = aNewDuration
}
Caesaria answered 25/11, 2013 at 21:58 Comment(6)
@Rich I can still reproduce the issue on iOS SDK 7.1 on an iPhone running iOS 7.1. What configuration do you have where this issue has been fixed?Cavefish
That is quite weird! This bug still exists on iOS 7.1, but the fix in this answer worked for me. Thanks.Lanita
I experience the same problem with Xcode6.1.1 and iOS 8.1Loach
anyone know if a radar was opened for this?Lubalubba
Adding on to this, it looks like the above fix works fine in the 90% case. The issue still occurs if the picker is shown, a rotation occurs, and the user goes to change the value.Lubalubba
There is still a separate bug in iOS 9 which happens when you try to select the time at 00:00 (which is below my minimum interval of 10 minutes). So the date picker automatically switches to 00:10, after that, when you try to switch to 02:00 for example, the value changed event won’t get called. This happens every time the date picker goes below the interval. Here is the workaround. - (IBAction)datePickerValueChanged:(UIDatePicker *)sender { if (sender.countDownDuration == (sender.minuteInterval * 60)) { sender.countDownDuration = sender.countDownDuration; } }Turne
U
6

I was still hitting this issue in 7.1 but adding the following to the UIControlEventValueChanged handler fixed it for me.

// Value Changed Event is not firing if minimum value hit
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    [self.myDatePicker setCountDownDuration: self.myDatePicker.countDownDuration];
});
Utica answered 1/4, 2014 at 5:39 Comment(3)
This worked for me under iOS 8, whereas Positron's solution didn't.Handedness
same here, it seems the delay is neededKensell
This worked for me, or in the same approach, if you don't want to delay just run it in the main queue dispatch_async(dispatch_get_main_queue(), ^{ [keyboard setCountDownDuration: keyboard.countDownDuration]; });Wunderlich
L
2

If someone still having problems with datepicker... I'm using Swift / iOS 8 / Xcode 6.3

So solve the problem you should no use

picker.countDownDuration = NSTimeInterval

instead, use setDate

picker.setDate(NSDate, animated: true)

it works direct on viewDidLoad(), don't need to use `queues

Lexi answered 15/7, 2015 at 22:33 Comment(0)
M
2

For Swift 3:

DispatchQueue.main.async(execute: {
    yourPicker.countDownDuration = TimeInterval() 
})
Michaelson answered 3/4, 2017 at 14:41 Comment(0)
F
2

Since no answer has been accepted yet. The simplest solution that worked for me in swift 3 is to simply do

datePicker.countDownDuration = seconds

in viewDidAppear instead of viewDidLoad

Fantan answered 25/6, 2017 at 23:22 Comment(0)
L
0

If someone still having problems with datepicker... I'm using Swift / iOS 8 / Xcode 6.3

To solve the problem you should not use picker.countDownDuration = NSTimeInterval. Use .setDate(NSDate, animated: true).

it works direct on viewDidLoad(), don't need to use queues

The complete snippet:

override func viewDidLoad() {
    super.viewDidLoad()
    picker.setDate(setDateFromSeconds(seconds), animated: true)
}

func setDateFromSeconds(seconds: Double) -> (NSDate) {
    let intSeconds = Int(seconds)
    let minutes = (intSeconds / 60) % 60
    let hours = intSeconds / 3600
    let dateString = NSString(format: "%0.2d:%0.2d", hours, minutes)

    let dateFormatter = NSDateFormatter()
    dateFormatter.dateFormat = "hh:mm"
    return dateFormatter.dateFromString(dateString as String) as NSDate!
}
Lexi answered 15/7, 2015 at 22:37 Comment(2)
Thanks for including the swift code for time interval to date conversion. This worked for me. Crazy that this problem still happens in iOS 10.Stpierre
This didn't solve the problem for me on iOS 14Seka
T
-2

Swift 4

@IBOutlet weak var fromPickerView: UIDatePicker!


@objc func toPickerViewDateChanged() {
        fromPickerView.minimumDate = toPickerView.date
    }
    override func viewDidLoad() {
        super.viewDidLoad()



toPickerView.backgroundColor = UIColor.white
    toPickerView.tintColor = .black
    toPickerView.maximumDate = Date()
    toPickerView.addTarget(self, action: 
    #selector(toPickerViewDateChanged), for: UIControlEvents.valueChanged)
Taeniasis answered 11/12, 2017 at 7:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.