UIDatePicker bug? UIControlEventValueChanged after

2019-01-16 19:23发布

问题:

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?

回答1:

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 ;
});


回答2:

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];
});


回答3:

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



回答4:

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!
}


回答5:

For Swift 3:

DispatchQueue.main.async(execute: {
    yourPicker.countDownDuration = TimeInterval() 
})


回答6:

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



回答7:

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)