Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/105.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 如何重写UIDatePicker组件?_Objective C_Ios_Uipickerview_Uidatepicker - Fatal编程技术网

Objective c 如何重写UIDatePicker组件?

Objective c 如何重写UIDatePicker组件?,objective-c,ios,uipickerview,uidatepicker,Objective C,Ios,Uipickerview,Uidatepicker,我注意到在iOS 5.0或5.1中。我决定试着写我自己的。我对如何填充数据以及如何以一种理智和内存高效的方式维护日期标签感到困惑 每个组件中实际有多少行?这些行什么时候会“重新加载”新标签 我将对此进行一次尝试,我会在发现后发布,但如果您知道任何事情,请发布。我猜您使用的是UIPickerView UIPickerView的工作原理与UITableView类似,因为您指定另一个类作为数据源/委托,它通过调用该类上的方法(该类应符合UIPickerViewDeleteGate/dataSource

我注意到在iOS 5.0或5.1中。我决定试着写我自己的。我对如何填充数据以及如何以一种理智和内存高效的方式维护日期标签感到困惑

每个组件中实际有多少行?这些行什么时候会“重新加载”新标签


我将对此进行一次尝试,我会在发现后发布,但如果您知道任何事情,请发布。

我猜您使用的是UIPickerView

UIPickerView的工作原理与UITableView类似,因为您指定另一个类作为数据源/委托,它通过调用该类上的方法(该类应符合UIPickerViewDeleteGate/dataSource协议)来获取数据

因此,您应该创建一个新类,它是UIPickerView的子类,然后在initWithFrame方法中,将其设置为自己的数据源/委托,然后实现这些方法

如果您以前没有使用过UITableView,那么应该熟悉datasource/delegate模式,因为这有点棘手,但是一旦您理解了它,就很容易使用

如果有帮助的话,我已经编写了一个自定义UIPicker来选择国家。这与你想在课堂上学习的方式基本相同,只是我使用了一个国家名称数组,而不是日期:


关于内存问题,UIPickerView仅加载屏幕上可见的标签,并在标签从底部滚动到顶部时对其进行回收,每次需要显示新行时,都会再次调用代理。这非常节省内存,因为一次屏幕上只有几个标签。

首先,感谢您提交有关UIDatePicker和希伯来日历的错误。:)


EDIT现在iOS 6已经发布,您会发现
UIDatePicker
现在可以正确使用希伯来日历,因此下面的代码不再需要。然而,我将把它留给子孙后代


正如您所发现的,创建一个功能正常的日期选择器是一个困难的问题,因为有无数奇怪的边缘案例需要覆盖。希伯来历法在这方面特别奇怪,有一个(阿达尔一世),而大多数西方文明习惯于每四年增加一天

这就是说,假设您愿意放弃
UIDatePicker
提供的一些细节,那么创建一个最小的希伯来语日期选择器并不太复杂。让我们简单地说:

@interface HPDatePicker : UIPickerView

@property (nonatomic, retain) NSDate *date;

- (void)setDate:(NSDate *)date animated:(BOOL)animated;

@end
我们只是将
UIPickerView
子类化,并添加对
date
属性的支持。我们将忽略
最小日期
最大日期
地区
日历
时区
,以及
UIDatePicker
提供的所有其他有趣属性。这将使我们的工作更简单

实现将从类扩展开始:

@interface HPDatePicker () <UIPickerViewDelegate, UIPickerViewDataSource>

@end
您可以看到,我们将对日历单位的顺序进行硬编码。换句话说,此日期选择器将始终以月-日-年的形式显示内容,而不考虑用户可能具有的任何自定义设置或区域设置。因此,如果您在默认格式为“日-月-年”的区域设置中使用此选项,那就太糟糕了。对于这个简单的例子,这就足够了

现在我们开始实施:

@implementation HPDatePicker {
    NSCalendar *hebrewCalendar;
    NSDateFormatter *formatter;

    NSRange maxDayRange;
    NSRange maxMonthRange;
}

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
        [self setDelegate:self];
        [self setDataSource:self];

        [self setShowsSelectionIndicator:YES];

        hebrewCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSHebrewCalendar];
        formatter = [[NSDateFormatter alloc] init];
        [formatter setCalendar:hebrewCalendar];

        maxDayRange = [hebrewCalendar maximumRangeOfUnit:NSDayCalendarUnit];
        maxMonthRange = [hebrewCalendar maximumRangeOfUnit:NSMonthCalendarUnit];

        [self setDate:[NSDate date]];
    }
    return self;
}

- (void)dealloc {
    [hebrewCalendar release];
    [formatter release];
    [super dealloc];
}
我们正在覆盖指定的初始值设定项,以便为我们进行一些设置。我们将委托和数据源设置为自己,显示选择指示符,并创建希伯来文日历对象。我们还创建了一个
NSDateFormatter
,并告诉它应该根据希伯来日历格式化
NSDates
。我们还提取了几个
NSRange
对象,并将它们缓存为IVAR,这样我们就不必经常查找。最后,我们用当前日期初始化它

以下是公开方法的实现:

- (void)setDate:(NSDate *)date {
    [self setDate:date animated:NO];
}
-setDate:
只需转发到另一个方法

- (NSDate *)date {
    NSDateComponents *c = [self selectedDateComponents];
    return [hebrewCalendar dateFromComponents:c];
}
检索表示当前所选内容的
NSDateComponents
,将其转换为
NSDate
,然后返回该内容

- (void)setDate:(NSDate *)date animated:(BOOL)animated {
    NSInteger units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
    NSDateComponents *components = [hebrewCalendar components:units fromDate:date];

    {
        NSInteger yearRow = [components year] - 1;
        [self selectRow:yearRow inComponent:YEAR_COMPONENT animated:animated];
    }

    {
        NSInteger middle = floor([self pickerView:self numberOfRowsInComponent:MONTH_COMPONENT] / 2);
        NSInteger startOfPhase = middle - (middle % maxMonthRange.length) - maxMonthRange.location;
        NSInteger monthRow = startOfPhase + [components month];
        [self selectRow:monthRow inComponent:MONTH_COMPONENT animated:animated];
    }

    {
        NSInteger middle = floor([self pickerView:self numberOfRowsInComponent:DAY_COMPONENT] / 2);
        NSInteger startOfPhase = middle - (middle % maxDayRange.length) - maxDayRange.location;
        NSInteger dayRow = startOfPhase + [components day];
        [self selectRow:dayRow inComponent:DAY_COMPONENT animated:animated];
    }
}
这就是有趣的事情开始发生的地方

首先,我们将获取给定的日期,并要求希伯来日历将其分解为date components对象。如果我给它一个
NSDate
,对应于2012年4月4日的公历日期,那么希伯来日历将给我一个
NSDateComponents
对象,对应于12 Nisan 5772,与2012年4月4日是同一天

根据这些信息,我决定在每个单元中选择哪一行,然后选择它。今年的情况很简单。我只需减去一(行是以零为基础的,但年份是以1为基础的)

对于月份,我选择行列的中间部分,找出序列的起始位置,并将月份号添加到其中。日子也是这样

基本
方法的实现相当简单。我们将显示3个组件,每个组件有10000行

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 3;
}

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return LARGE_NUMBER_OF_ROWS;
}
获取当前选中的内容相当简单。我在每个组件中获取所选行,然后添加1(在
NSYearCalendarUnit
的情况下),或者执行一点mod操作以说明其他日历单元的重复性质

- (NSDateComponents *)selectedDateComponents {
    NSDateComponents *c = [[NSDateComponents alloc] init];

    [c setYear:[self selectedRowInComponent:YEAR_COMPONENT] + 1];

    NSInteger monthRow = [self selectedRowInComponent:MONTH_COMPONENT];
    [c setMonth:(monthRow % maxMonthRange.length) + maxMonthRange.location];

    NSInteger dayRow = [self selectedRowInComponent:DAY_COMPONENT];
    [c setDay:(dayRow % maxDayRange.length) + maxDayRange.location];

    return [c autorelease];
}
最后,我需要在UI中显示一些字符串:

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    NSString *format = nil;
    NSDateComponents *c = [[NSDateComponents alloc] init];

    if (component == YEAR_COMPONENT) {
        format = @"y";
        [c setYear:row+1];
        [c setMonth:1];
        [c setDay:1];        
    } else if (component == MONTH_COMPONENT) {
        format = @"MMMM";
        [c setYear:5774];
        [c setMonth:(row % maxMonthRange.length) + maxMonthRange.location];
        [c setDay:1];
    } else if (component == DAY_COMPONENT) {
        format = @"d";
        [c setYear:5774];
        [c setMonth:1];
        [c setDay:(row % maxDayRange.length) + maxDayRange.location];
    }

    NSDate *d = [hebrewCalendar dateFromComponents:c];
    [c release];

    [formatter setDateFormat:format];

    NSString *title = [formatter stringFromDate:d];

    return title;
}

@end
这就是事情有点复杂的地方。不幸的是,
NSDateFormatter
只能在给定实际的
NSDate
时格式化内容。我不能只说“这是6”而希望得到“阿达尔I”。因此,我必须构建一个人工日期,该日期在我关心的单元中具有我想要的值

以年为例,这很简单。只要在Tishri 1上创建当年的日期组件,我就可以了

好几个月来,我都要做苏
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    NSString *format = nil;
    NSDateComponents *c = [[NSDateComponents alloc] init];

    if (component == YEAR_COMPONENT) {
        format = @"y";
        [c setYear:row+1];
        [c setMonth:1];
        [c setDay:1];        
    } else if (component == MONTH_COMPONENT) {
        format = @"MMMM";
        [c setYear:5774];
        [c setMonth:(row % maxMonthRange.length) + maxMonthRange.location];
        [c setDay:1];
    } else if (component == DAY_COMPONENT) {
        format = @"d";
        [c setYear:5774];
        [c setMonth:1];
        [c setDay:(row % maxDayRange.length) + maxDayRange.location];
    }

    NSDate *d = [hebrewCalendar dateFromComponents:c];
    [c release];

    [formatter setDateFormat:format];

    NSString *title = [formatter stringFromDate:d];

    return title;
}

@end