【问题标题】:UIDatePicker odd behavior when setting minuteInterval设置 minuteInterval 时的 UIDatePicker 奇怪行为
【发布时间】:2011-08-04 20:46:12
【问题描述】:

以下代码在 iOS 4.3(可能其他版本)下显示了一个奇怪的行为。在此示例中,显示日期设置为4 Aug 2011 2:31 PMUIDatePickerUIDatePicker 下方的 UILabel 显示日期以供参考。下面三个UIButtons,分别标记为1、5、10,将minuteInterval设置在UIDatePicker上。

点击 1 - 显示UIDatePicker 中选择的日期为4 Aug 2011 2:31 PM,分钟间隔为 1,这是意料之中的。

点击 5 - 将 UIDatePicker 中的选定日期显示为 4 Aug 2011 2:35 PM,分钟间隔为 5,这是可以预期的(有人可能会争辩说时间应该四舍五入,但这不是一个大问题)。

点击 10 - 显示UIDatePicker 中选择的日期为4 Aug 2011 2:10 PM,分钟间隔为 10。好的分钟间隔正确,但选择的时间是 2:10?人们会期望 2:40(如果向上取整)或 2:30(如果向下取整)。

BugDatePickerVC.h

#import <UIKit/UIKit.h>

@interface BugDatePickerVC : UIViewController {
    NSDateFormatter *dateFormatter;
    NSDate *date;
    UIDatePicker *datePicker;
    UILabel *dateL;
    UIButton *oneB;
    UIButton *fiveB;
    UIButton *tenB;
}

- (void) buttonEventTouchDown:(id)sender;

@end

BugDatePickerVC.m

导入“BugDatePickerVC.h”

@implementation BugDatePickerVC

- (id) init
{
    if ( !(self = [super init]) )
    {
        return self;
    }

    dateFormatter = [[NSDateFormatter alloc] init];
    dateFormatter.dateFormat = @"d MMM yyyy h:mm a";

    date = [[dateFormatter dateFromString:@"4 Aug 2011 2:31 PM"] retain];

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Date picker
    datePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 216.0f)];
    datePicker.date = date;
    datePicker.minuteInterval = 1;
    [self.view addSubview:datePicker];

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Label with the date.
    dateL = [[UILabel alloc] initWithFrame:CGRectMake(10.0f, 230.0f, 300.0f, 32.0f)];
    dateL.text = [dateFormatter stringFromDate:date];
    [self.view addSubview:dateL];

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Button that set the date picker's minute interval to 1.
    oneB = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    oneB.frame = CGRectMake(10.0f, 270.0f, 100.0f, 32.0f);
    oneB.tag = 1;
    [oneB setTitle:@"1" forState:UIControlStateNormal];
    [oneB   addTarget:self
               action:@selector(buttonEventTouchDown:)
     forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:oneB];

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Button that set the date picker's minute interval to 5.
    fiveB = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    fiveB.frame = CGRectMake(10.0f, 310.0f, 100.0f, 32.0f);
    fiveB.tag = 5;
    [fiveB setTitle:@"5" forState:UIControlStateNormal];
    [fiveB  addTarget:self
               action:@selector(buttonEventTouchDown:)
     forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:fiveB];

    // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
    // Button that set the date picker's minute interval to 10.
    tenB = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    tenB.frame = CGRectMake(10.0f, 350.0f, 100.0f, 32.0f);
    tenB.tag = 10;
    [tenB setTitle:@"10" forState:UIControlStateNormal];
    [tenB   addTarget:self
               action:@selector(buttonEventTouchDown:)
     forControlEvents:UIControlEventTouchDown];
    [self.view addSubview:tenB];

    return self;
}

- (void) dealloc
{
    [dateFormatter release];
    [date release];
    [datePicker release];
    [dateL release];
    [oneB release];
    [fiveB release];
    [tenB release];

    [super dealloc];
}

- (void) buttonEventTouchDown:(id)sender
{
    datePicker.minuteInterval = [sender tag];
}

【问题讨论】:

    标签: ios uidatepicker


    【解决方案1】:

    好的,我可以通过使用以下代码将 UIDatePicker 日期值明确设置为四舍五入到分钟间隔的日期来更改行为:

    - (void) handleUIControlEventTouchDown:(id)sender
    {
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // Set the date picker's minute interval.
        NSInteger minuteInterval  = [sender tag];
    
        // Setting the date picker's minute interval can change what is selected on
        // the date picker's UI to a wrong date, it does not effect the date
        // picker's date value.
        //
        // For example the date picker's date value is 2:31 and then minute interval
        // is set to 10.  The date value is still 2:31, but 2:10 is selected on the
        // UI, not 2:40 (rounded up) or 2:30 (rounded down).
        //
        // The code that follow's setting the date picker's minute interval
        // addresses fixing the date value (and the selected date on the UI display)
        // .  In the example above both would be 2:30.
        datePicker.minuteInterval = minuteInterval;
    
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // Calculate the proper date value (and the date to be selected on the UI
        // display) by rounding down to the nearest minute interval.
        NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:date];
        NSInteger minutes = [dateComponents minute];
        NSInteger minutesRounded = ( (NSInteger)(minutes / minuteInterval) ) * minuteInterval;
        NSDate *roundedDate = [[NSDate alloc] initWithTimeInterval:60.0 * (minutesRounded - minutes) sinceDate:date];
    
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // Set the date picker's value (and the selected date on the UI display) to
        // the rounded date.
        if ([roundedDate isEqualToDate:datePicker.date])
        {
            // We need to set the date picker's value to something different than
            // the rounded date, because the second call to set the date picker's
            // date with the same value is ignored. Which could be bad since the
            // call above to set the date picker's minute interval can leave the
            // date picker with the wrong selected date (the whole reason why we are
            // doing this).
            NSDate *diffrentDate = [[NSDate alloc] initWithTimeInterval:60 sinceDate:roundedDate];
            datePicker.date = diffrentDate;
            [diffrentDate release];
        }
        datePicker.date = roundedDate;
        [roundedDate release];
    }
    

    注意UIDatePicker的日期设置了两次的部分。弄清楚这一点很有趣。

    有人知道如何关闭调用minuteInterval 的动画吗?点 5 再点 10 的幻影滚动有点难看。

    【讨论】:

      【解决方案2】:

      我使用了 mmoris 的上述解决方案并创建了一个返回四舍五入日期的方法..(对于 ARC)

      - (NSDate *)getRoundedDate:(NSDate *)inDate{
      
          NSDate *returnDate;
          NSInteger minuteInterval = 10;
          NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:inDate];
          NSInteger minutes = [dateComponents minute];
          NSInteger minutesRounded = ( (NSInteger)(minutes / minuteInterval) ) * minuteInterval;
          NSDate *roundedDate = [[NSDate alloc] initWithTimeInterval:60.0 * (minutesRounded - minutes) sinceDate:inDate];
      
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          // Set the date picker's value (and the selected date on the UI display) to
          // the rounded date.
          if ([roundedDate isEqualToDate:inDate])
          {
             // We need to set the date picker's value to something different than
             // the rounded date, because the second call to set the date picker's
             // date with the same value is ignored. Which could be bad since the
             // call above to set the date picker's minute interval can leave the
             // date picker with the wrong selected date (the whole reason why we are
             // doing this).
             NSDate *diffrentDate = [[NSDate alloc] initWithTimeInterval:60 sinceDate:roundedDate];
             returnDate = diffrentDate;
             //[diffrentDate release];
          }
      
          returnDate = roundedDate;
          return returnDate;
      }
      

      【讨论】:

        【解决方案3】:

        这是另一种方法,使用 Objective-C 类别!

        我借鉴了@zurbergram 的舍入行为(向上/向下最接近)和@mmorris 的整体答案,并提出了这个类别:

        #import <UIKit/UIKit.h>
        
        @interface UIDatePicker (SetDateRounded)
        
        -(void)setMinimumDateRoundedByMinuteInterval:(NSDate *)minimumDate;
        -(void)setDateRoundedByMinuteInterval:(NSDate *)date animated:(BOOL)animatedYesNo;
        
        @end
        
        @implementation UIDatePicker (SetDateRounded)
        
        -(void)setDateRoundedByMinuteInterval:(NSDate *)date animated:(BOOL)animatedYesNo
        {
            NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:date];
            NSInteger minutes = [dateComponents minute];
            NSInteger minutesRounded = roundf((float)minutes / (float)[self minuteInterval]) * self.minuteInterval;
            NSDate *roundedDate = [[NSDate alloc] initWithTimeInterval:60.0 * (minutesRounded - minutes) sinceDate:date];
            [self setDate:roundedDate animated:animatedYesNo];
        }
        
        -(void)setMinimumDateRoundedByMinuteInterval:(NSDate *)date
        {
            NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:date];
            NSInteger minutes = [dateComponents minute];
            NSInteger minutesRounded = roundf((float)minutes / (float)[self minuteInterval]) * self.minuteInterval;
            NSDate *roundedDate = [[NSDate alloc] initWithTimeInterval:60.0 * (minutesRounded - minutes) sinceDate:date];
            [self setMinimumDate:roundedDate];
        }
        
        @end
        

        然后在你的实现中,你可以这样做:

        #import "UIDatePicker+SetDateRounded.h"
        
        ...
        
        - (void)viewDidLoad
        {
            [super viewDidLoad];
        
            _datePicker.minuteInterval = 15;
        
            [_datePicker setMinimumDateRoundedByMinuteInterval:[NSDate date]];
            [_datePicker setDateRoundedByMinuteInterval:[NSDate date] animated:YES];
        }
        
        ...
        

        奖励方法:setMinimumDateRoundedByMinuteInterval:让您设置选取器的初始最小值以匹配相同的行为。一种重构是将实际计算部分抽象为自己的方法,而不是复制意大利面,但我相信人们可以自己优化它。

        【讨论】:

          【解决方案4】:

          这是 getRoundedDate 的更新版本:向上或向下舍入,以便下午 1:03 向下舍入为下午 1:00,下午 1:12 向上舍入为下午 1:15

          -(NSDate *)getRoundedDate:(NSDate *)inDate
          {
              NSInteger minuteInterval = 15;
              NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSMinuteCalendarUnit fromDate:inDate];
              NSInteger minutes = [dateComponents minute];
          
              float minutesF = [[NSNumber numberWithInteger:minutes] floatValue];
              float minuteIntervalF = [[NSNumber numberWithInteger:minuteInterval] floatValue];
          
              // Determine whether to add 0 or the minuteInterval to time found by rounding down
              NSInteger roundingAmount = (fmodf(minutesF, minuteIntervalF)) > minuteIntervalF/2.0 ? minuteInterval : 0;
              NSInteger minutesRounded = ( (NSInteger)(minutes / minuteInterval) ) * minuteInterval;
              NSDate *roundedDate = [[NSDate alloc] initWithTimeInterval:60.0 * (minutesRounded + roundingAmount - minutes) sinceDate:inDate];
          
              return roundedDate;
          }
          

          【讨论】:

            【解决方案5】:

            我在使用 UIDatePicker 时遇到了同样的问题,只有小时和分钟,每次我选择一个时间时,选择器都会在 UI 中添加 20 分钟,而不是在选择的时间。就我而言,解决方案非常简单,在设置选择器的值之前设置picker.minuteInterval=5

            希望这对其他人有所帮助。

            【讨论】:

              【解决方案6】:
              Swift 中的

              zurberggram 代码:

              func getRoundedDate(inDate: NSDate) -> NSDate {
              
                  let minuteInterval = 15
                  let dateComponents = NSCalendar.currentCalendar().components(NSCalendarUnit.MinuteCalendarUnit, fromDate: inDate)
                  let minutes = dateComponents.minute
              
                  let minutesF = NSNumber(integer: minutes).floatValue
                  let minuteIntervalF = NSNumber(integer: minuteInterval).floatValue
              
                  // Determine whether to add 0 or the minuteInterval to time found by rounding down
                  let roundingAmount = (fmodf(minutesF, minuteIntervalF)) > minuteIntervalF/2.0 ? minuteInterval : 0
                  let minutesRounded = (minutes / minuteInterval) * minuteInterval
                  let timeInterval = NSNumber(integer: (60 * (minutesRounded + roundingAmount - minutes))).doubleValue
                  let roundedDate = NSDate(timeInterval: timeInterval, sinceDate: inDate )
              
                  return roundedDate
              }
              

              【讨论】:

                【解决方案7】:

                Swift 4 版本

                func round(date: Date, for minuteInterval: Int) -> Date {
                        let dateComponents = Calendar.current.dateComponents([.minute], from: date)
                
                        let minutes = dateComponents.minute!
                
                        // Determine whether to add 0 or the minuteInterval to time found by rounding down
                
                        let intervalRemainder = Double(minutes).truncatingRemainder(
                            dividingBy: Double(minuteInterval)
                        )
                
                        let halfInterval = Double(minuteInterval) / 2.0
                
                        let roundingAmount: Int
                        if  intervalRemainder > halfInterval {
                            roundingAmount = minuteInterval
                        } else {
                            roundingAmount = 0
                        }
                
                        let minutesRounded = minutes / minuteInterval * minuteInterval
                        let timeInterval = TimeInterval(
                            60 * (minutesRounded + roundingAmount - minutes)
                        )
                
                        let roundedDate = Date(timeInterval: timeInterval, since: date)
                
                        return roundedDate
                    }
                

                【讨论】:

                  猜你喜欢
                  • 2013-10-30
                  • 2012-09-30
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-02-10
                  • 1970-01-01
                  • 2014-12-31
                  • 1970-01-01
                  相关资源
                  最近更新 更多