पहले, बग के बारे में UIDatePicker
और हिब्रू कैलेंडर दाखिल करने के लिए धन्यवाद। :)
संपादित अब जब कि IOS 6 जारी की गई है, तो आप उस UIDatePicker
अब हिब्रू कैलेंडर के साथ सही ढंग से काम करता है, अनावश्यक नीचे कोड बनाने मिल जाएगा। हालांकि, मैं इसे जन्म के लिए छोड़ दूंगा। क्योंकि वहाँ अजीब बढ़त के मामलों की bajillions कवर करने के लिए कर रहे हैं
आप की खोज की है के रूप में, एक कामकाज दिनांक पिकर बनाने, एक कठिन समस्या है। हिब्रू कैलेंडर विशेष रूप से अजीब है, जिसमें intercalary month (अदार I) है, जबकि पश्चिमी सभ्यता का अधिकांश कैलेंडर कैलेंडर में उपयोग किया जाता है जो केवल 4 वर्षों में एक बार अतिरिक्त दिन जोड़ता है।
कहा जा रहा है कि, कम से कम हिब्रू डेट पिकर बनाना बहुत जटिल नहीं है, मानते हुए कि आप UIDatePicker
ऑफ़र की कुछ नस्लों से आगे निकलना चाहते हैं। तो चलो इसे सरल बनाते हैं:
@interface HPDatePicker : UIPickerView
@property (nonatomic, retain) NSDate *date;
- (void)setDate:(NSDate *)date animated:(BOOL)animated;
@end
हम बस UIPickerView
उपवर्ग और एक date
संपत्ति के लिए समर्थन जोड़ने के लिए जा रहे हैं।हम minimumDate
, maximumDate
, locale
, calendar
, timeZone
, और UIDatePicker
प्रदान करने वाले अन्य सभी मजेदार गुणों को अनदेखा करने जा रहे हैं। इससे हमारा काम अधिक आसान हो जाएगा।
कार्यान्वयन एक वर्ग विस्तार के साथ शुरू करने जा रहा है:
@interface HPDatePicker() <UIPickerViewDelegate, UIPickerViewDataSource>
@end
आप को छिपाने के लिए है कि HPDatePicker
अपने स्वयं के प्रतिनिधि और डेटा स्रोत है।
#define LARGE_NUMBER_OF_ROWS 10000
#define MONTH_COMPONENT 0
#define DAY_COMPONENT 1
#define YEAR_COMPONENT 2
आप यहाँ है कि हम कड़ी मेहनत से कोड के लिए कैलेंडर इकाइयों के आदेश जा रहे हैं देख सकते हैं:
अगला हम काम स्थिरांक के एक जोड़े को परिभाषित करेंगे। दूसरे शब्दों में, यह दिनांक पिकर हमेशा किसी भी अनुकूलन या उपयोगकर्ता की स्थानीय सेटिंग्स के बावजूद, चीज़ें-दिवस-वर्ष के रूप में प्रदर्शित करेगा। तो यदि आप इसे लोकेल में उपयोग कर रहे हैं जहां डिफ़ॉल्ट प्रारूप "दिन-महीना-वर्ष" चाहता है, तो बहुत बुरा। इस सरल उदाहरण के लिए, यह पर्याप्त होगा।
@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
ऑब्जेक्ट्स को भी खींचते हैं और उन्हें ivars के रूप में कैश करते हैं, इसलिए हमें लगातार चीजों को देखने की ज़रूरत नहीं है। अंत में, हम इसे वर्तमान तारीख के साथ शुरू करते हैं।
- (void)setDate:(NSDate *)date {
[self setDate:date animated:NO];
}
अन्य विधि
- (NSDate *)date {
NSDateComponents *c = [self selectedDateComponents];
return [hebrewCalendar dateFromComponents:c];
}
को -setDate:
बस आगे पर पुन: प्राप्त एक 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];
}
}
और यह वह जगह है जहां मजेदार सामान शुरू हो रहा है।
सबसे पहले, हम जो तारीख दी गई थी उसे हम लेंगे और हेब्रू कैलेंडर को तारीख घटकों के ऑब्जेक्ट में तोड़ने के लिए कहेंगे। अगर मैं यह एक NSDate
कि 4 अप्रैल 2012 के ग्रेगोरी तिथि से मेल खाती है देते हैं, तो हिब्रू कैलेंडर मुझे एक NSDateComponents
उद्देश्य यह है कि निसान 5772 12 से मेल खाती है, जो 4 के रूप में एक ही दिन है अप्रैल 2012
देने के लिए जा रहा है इस जानकारी के आधार पर, मुझे पता चलता है कि प्रत्येक इकाई में कौन सी पंक्ति का चयन करना है, और इसे चुनें। साल का मामला सरल है। मैं बस एक घटाता हूं (पंक्तियां शून्य-आधारित होती हैं, लेकिन वर्ष 1-आधारित होती हैं)।
महीनों के लिए, मैं पंक्तियों के कॉलम के बीच को चुनता हूं, यह पता लगाता हूं कि यह अनुक्रम कहां से शुरू होता है, और इसमें महीना संख्या जोड़ें। दिनों के साथ ही वही।
आधार <UIPickerViewDataSource>
विधियों के कार्यान्वयन काफी मामूली हैं। हम 3 घटक प्रदर्शित कर रहे हैं, और प्रत्येक में 10,000 पंक्तियां हैं।
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 3;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return LARGE_NUMBER_OF_ROWS;
}
वर्तमान क्षण में जो भी चुना गया है वह काफी सरल है। मुझे प्रत्येक घटक में चयनित पंक्ति मिलती है और या तो 1 (NSYearCalendarUnit
के मामले में) जोड़ें, या अन्य कैलेंडर इकाइयों की दोहराने वाली प्रकृति के लिए थोड़ा सा मॉड ऑपरेशन करें।
- (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 नहीं" कह सकता हूं और "एडार आई" वापस पाने की उम्मीद करता हूं। इस प्रकार, मुझे एक कृत्रिम तारीख बनाना है जिसमें उस इकाई में मूल्य है जो मैं चाहता हूं।
वर्षों के मामले में, यह बहुत आसान है। बस उस वर्ष के लिए टिशरी 1 पर डेट घटक बनाएं, और मैं अच्छा हूं।
महीनों के लिए, मुझे यह सुनिश्चित करना होगा कि वर्ष एक लीप वर्ष है। ऐसा करके, मैं गारंटी दे सकता हूं कि महीने का नाम हमेशा "एडार आई" और "एडार II" होगा, भले ही वर्तमान वर्ष लीप वर्ष हो या नहीं।
दिनों के लिए, मैंने मनमाने ढंग से वर्ष चुना, क्योंकि हर तिशरी में 30 दिन होते हैं (और हिब्रू कैलेंडर में कोई भी महीना 30 दिनों से अधिक नहीं होता है)।
एक बार जब हम उपयुक्त तिथि घटकों वस्तु का निर्माण किया है, हम जल्दी से यह एक NSDate
में हमारे hebrewCalendar
इवर साथ, बारी केवल इकाई हम के बारे में परवाह के लिए तार का निर्माण किया जाना है तारीख फ़ॉर्मेटर पर प्रारूप स्ट्रिंग सेट है, और उत्पन्न कर सकते हैं एक स्ट्रिंग।
मान लें कि आप यह सब ठीक से किया है, तो आप इस के साथ खत्म हो जाएगा:

कुछ नोट:
मैं कार्यान्वयन बाहर छोड़ दिया है -pickerView:didSelectRow:inComponent:
का। यह पता लगाने के लिए आप कैसे सूचित करना चाहते हैं कि चयनित तिथि बदल गई है।
यह अमान्य तिथियों को ग्रेइंग को संभाल नहीं करता है। उदाहरण के लिए, यदि आप वर्तमान में चयनित वर्ष लीप वर्ष नहीं है तो आप "एडार I" को भूरे रंग पर विचार करना चाहेंगे। titleForRow:
विधि के बजाय इसे -pickerView:viewForRow:inComponent:reusingView:
का उपयोग करने की आवश्यकता होगी।
UIDatePicker
नीली रंग की वर्तमान तारीख को हाइलाइट करेगा। फिर, आपको ऐसा करने के लिए स्ट्रिंग के बजाय कस्टम दृश्य वापस करना होगा।
आपकी तिथि पिकर में ब्लैकिश बीज़ल होगा, क्योंकि यह UIPickerView
है। केवल UIDatePickers
नीली हो जाओ।
पिकर व्यू के घटक इसकी पूरी चौड़ाई बढ़ाएंगे। यदि आप चीजों को अधिक स्वाभाविक रूप से फिट करना चाहते हैं, तो आपको उचित घटक के लिए एक सौ मूल्य वापस करने के लिए -pickerView:widthForComponent:
ओवरराइड करना होगा। इसमें मूल्य को हार्ड कोडिंग करना या सभी तारों को उत्पन्न करना, उनमें से प्रत्येक का आकार बनाना और सबसे बड़ा (प्लस कुछ पैडिंग) चुनना शामिल हो सकता है।
जैसा कि पहले उल्लेख किया गया है, यह हमेशा मासिक-वर्ष-वर्ष क्रम में चीजें प्रदर्शित करता है। इस गतिशील को वर्तमान लोकेल में बनाना थोड़ा सा ट्रिकियर होगा। आपको वर्तमान लोकेल में स्थानांतरित @"d MMMM y"
स्ट्रिंग प्राप्त करना होगा (संकेत: उस के लिए NSDateFormatter
पर कक्षा विधियों को देखें), और फिर ऑर्डर करने के लिए इसे पार्स करें।
यह स्टैक ओवरफ़्लो पर मैंने कभी देखा है सबसे अच्छा जवाब होना चाहिए। +1 –
यह कोड एआरसी, बीटीडब्ल्यू के साथ और भी बढ़िया हो जाता है। ;-) जुस 'कह रहा है। – Moshe
@ डेव डीलॉन्ग मेरा नायक है। –