I used the amended DateCell code from the following link DateCell without storyboard.
I want to replace UIDatePicker with UIPickerView. Below is the code that I have amended/commented to make it work for UIPickerView.
On each cell tap, I'm able to show UIPickerView but it only loads once and for the rest of the cells UIPickerView with same row titles appears and it crashes when I select a row from UIPickerView. I'm stuck into this for last couple of days.
Any help would be highly appreciated.
MoreTableViewController.h
#import <UIKit/UIKit.h> @interface MoreTableViewController : UITableViewController<UIPickerViewDataSource, UIPickerViewDelegate> @property(nonatomic,retain)NSArray *ASOR; @property(nonatomic,retain)NSArray *ASQT; @property(nonatomic,retain)NSArray *respectiveOptions; @end MoreTableViewController.m
#import "MoreTableViewController.h" #import "AppDelegate.h" #define kPickerAnimationDuration 0.40 // duration for the animation to slide the date picker into view #define kDatePickerTag 99 // view tag identifiying the date picker view //#define kTitleKey @"title" // key for obtaining the data source item's title //#define kDateKey @"date" // key for obtaining the data source item's date value // keep track of which rows have date cells #define kDateStartRow 1 #define kDateEndRow 2 static NSString *kDateCellID = @"dateCell"; // the cells with the start or end date static NSString *kDatePickerID = @"datePicker"; // the cell containing the date picker static NSString *kOtherCell = @"otherCell"; // the remaining cells at the end #pragma mark - @interface MoreTableViewController () @property (nonatomic, strong) NSArray *dataArray; //@property (nonatomic, strong) NSDateFormatter *dateFormatter; // keep track which indexPath points to the cell with UIDatePicker @property (nonatomic, strong) NSIndexPath *datePickerIndexPath; @property (assign) NSInteger pickerCellRowHeight; @property (nonatomic, strong) UIPickerView *pickerView; @end @implementation MoreTableViewController @synthesize ASOR, ASQT, respectiveOptions; - (UITableViewCell *) createCellWithIdetifier:(NSString *)cellId { NSArray *reusableUiComponents = [[NSBundle mainBundle] loadNibNamed:@"ReusableUIComponents" owner:self options:nil]; if ([kDateCellID isEqualToString:cellId]) { return reusableUiComponents[0]; } if ([kDatePickerID isEqualToString:cellId]) { return reusableUiComponents[1]; } // if ([kOtherCell isEqualToString:cellId]) { // return reusableUiComponents[2]; // } return nil; } - (AppDelegate *)appDelegate { return (AppDelegate *)[[UIApplication sharedApplication] delegate]; } - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (void)setupDataSource { // setup our data source // NSMutableDictionary *itemOne = [@{ kTitleKey : @"Tap a cell to change its date:" } mutableCopy]; // NSMutableDictionary *itemTwo = [@{ kTitleKey : @"Start Date", // kDateKey : [NSDate date] } mutableCopy]; // NSMutableDictionary *itemThree = [@{ kTitleKey : @"End Date", // kDateKey : [NSDate date] } mutableCopy]; // NSMutableDictionary *itemFour = [@{ kTitleKey : @"(other item1)" } mutableCopy]; // NSMutableDictionary *itemFive = [@{ kTitleKey : @"(other item2)" } mutableCopy]; self.dataArray = [[self appDelegate]advanceSearchQuestionsText]; //self.dateFormatter = [[NSDateFormatter alloc] init]; //[self.dateFormatter setDateStyle:NSDateFormatterShortStyle]; // show short-style date format //[self.dateFormatter setTimeStyle:NSDateFormatterNoStyle]; // obtain the picker view cell's height, works because the cell was pre-defined in our storyboard UITableViewCell *pickerViewCellToCheck = [self createCellWithIdetifier:kDatePickerID]; self.pickerCellRowHeight = pickerViewCellToCheck.frame.size.height; } - (void)viewDidLoad { [super viewDidLoad]; [self setupDataSource]; //self.title = @"DateCell"; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; ASQT = [[NSArray alloc]init]; ASOR = [[NSArray alloc]init]; ASQT = [[self appDelegate]advanceSearchQuestionsText]; ASOR = [[self appDelegate]advanceSearchOptionsArray]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Utilities /*! Returns the major version of iOS, (i.e. for iOS 6.1.3 it returns 6) */ NSUInteger UNUSED_DeviceSystemMajorVersion() // TODO - move this to Utils { static NSUInteger _deviceSystemMajorVersion = -1; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."] objectAtIndex:0] intValue]; }); return _deviceSystemMajorVersion; } /*! Determines if the given indexPath has a cell below it with a UIDatePicker. @param indexPath The indexPath to check if its cell has a UIDatePicker below it. */ - (BOOL)hasPickerForIndexPath:(NSIndexPath *)indexPath { BOOL hasDatePicker = NO; NSInteger targetedRow = indexPath.row; targetedRow++; UITableViewCell *checkDatePickerCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:targetedRow inSection:0]]; UIPickerView *checkDatePicker = (UIPickerView *)[checkDatePickerCell viewWithTag:kDatePickerTag]; hasDatePicker = (checkDatePicker != nil); return hasDatePicker; } /*! Updates the UIDatePicker's value to match with the date of the cell above it. */ - (void)updateDatePicker { if (self.datePickerIndexPath != nil) { UITableViewCell *associatedDatePickerCell = [self.tableView cellForRowAtIndexPath:self.datePickerIndexPath]; UIPickerView *targetedDatePicker = (UIPickerView *)[associatedDatePickerCell viewWithTag:kDatePickerTag]; if (targetedDatePicker != nil) { // we found a UIDatePicker in this cell, so update it's date value // // NSDictionary *itemData = self.dataArray[self.datePickerIndexPath.row - 1]; // [targetedDatePicker setDate:[itemData valueForKey:kDateKey] animated:NO]; // set the call action for the date picker to dateAction // [targetedDatePicker addTarget:self action:@selector(dateAction:) forControlEvents:UIControlEventValueChanged]; } } } /*! Determines if the UITableViewController has a UIDatePicker in any of its cells. */ - (BOOL)hasInlineDatePicker { return (self.datePickerIndexPath != nil); } /*! Determines if the given indexPath points to a cell that contains the UIDatePicker. @param indexPath The indexPath to check if it represents a cell with the UIDatePicker. */ - (BOOL)indexPathHasPicker:(NSIndexPath *)indexPath { return ([self hasInlineDatePicker] && self.datePickerIndexPath.row == indexPath.row); } /*! Determines if the given indexPath points to a cell that contains the start/end dates. @param indexPath The indexPath to check if it represents start/end date cell. */ - (BOOL)indexPathHasDate:(NSIndexPath *)indexPath { BOOL hasDate = NO; if ((indexPath.row == kDateStartRow) || (indexPath.row == kDateEndRow || ([self hasInlineDatePicker] && (indexPath.row == kDateEndRow + 1)))) { hasDate = YES; } return hasDate; } #pragma mark - Table view data source - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CGFloat height = ([self indexPathHasPicker:indexPath] ? self.pickerCellRowHeight : self.tableView.rowHeight); return height; } //- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView //{ // return 1; //} - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if ([self hasInlineDatePicker]) { // we have a date picker, so allow for it in the number of rows in this section NSInteger numRows = self.dataArray.count; return ++numRows; } return self.dataArray.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = nil; NSString *cellID = kDateCellID; if ([self indexPathHasPicker:indexPath]) { // the indexPath is the one containing the inline date picker cellID = kDatePickerID; // the current/opened date picker cell } else if ([self indexPathHasDate:indexPath]) { // the indexPath is one that contains the date information cellID = kDateCellID; // the start/end date cells } cell = [tableView dequeueReusableCellWithIdentifier:cellID]; if (!cell) { cell = [self createCellWithIdetifier:cellID]; } if (indexPath.row == 0) { // we decide here that first cell in the table is not selectable (it's just an indicator) cell.selectionStyle = UITableViewCellSelectionStyleNone; } // if we have a date picker open whose cell is above the cell we want to update, // then we have one more cell than the model allows // NSInteger modelRow = indexPath.row; if (self.datePickerIndexPath != nil && self.datePickerIndexPath.row < indexPath.row) { modelRow--; } NSDictionary *itemData = self.dataArray[modelRow]; // proceed to configure our cell if ([cellID isEqualToString:kDateCellID]) { // we have either start or end date cells, populate their date field // cell.textLabel.text = [self.dataArray objectAtIndex:indexPath.row]; //cell.detailTextLabel.text = [self.dateFormatter stringFromDate:[itemData valueForKey:kDateKey]]; } // else if ([cellID isEqualToString:kOtherCell]) // { // // this cell is a non-date cell, just assign it's text label // // // //cell.textLabel.text = [itemData valueForKey:kTitleKey]; // cell.textLabel.text = [self.dataArray objectAtIndex:indexPath.row]; // } return cell; } /*! Adds or removes a UIDatePicker cell below the given indexPath. @param indexPath The indexPath to reveal the UIDatePicker. */ - (void)toggleDatePickerForSelectedIndexPath:(NSIndexPath *)indexPath { [self.tableView beginUpdates]; NSArray *indexPaths = @[[NSIndexPath indexPathForRow:indexPath.row + 1 inSection:0]]; // check if 'indexPath' has an attached date picker below it if ([self hasPickerForIndexPath:indexPath]) { // found a picker below it, so remove it [self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; } else { // didn't find a picker below it, so we should insert it [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationFade]; } [self.tableView endUpdates]; } /*! Reveals the date picker inline for the given indexPath, called by "didSelectRowAtIndexPath". @param indexPath The indexPath to reveal the UIDatePicker. */ - (void)displayInlineDatePickerForRowAtIndexPath:(NSIndexPath *)indexPath { // display the date picker inline with the table content [self.tableView beginUpdates]; BOOL before = NO; // indicates if the date picker is below "indexPath", help us determine which row to reveal if ([self hasInlineDatePicker]) { before = self.datePickerIndexPath.row < indexPath.row; } BOOL sameCellClicked = (self.datePickerIndexPath.row - 1 == indexPath.row); // remove any date picker cell if it exists if ([self hasInlineDatePicker]) { [self.tableView deleteRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.datePickerIndexPath.row inSection:0]] withRowAnimation:UITableViewRowAnimationFade]; self.datePickerIndexPath = nil; } if (!sameCellClicked) { // hide the old date picker and display the new one NSInteger rowToReveal = (before ? indexPath.row - 1 : indexPath.row); NSIndexPath *indexPathToReveal = [NSIndexPath indexPathForRow:rowToReveal inSection:0]; [self toggleDatePickerForSelectedIndexPath:indexPathToReveal]; self.datePickerIndexPath = [NSIndexPath indexPathForRow:indexPathToReveal.row + 1 inSection:0]; } // always deselect the row containing the start or end date [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; [self.tableView endUpdates]; // inform our date picker of the current date to match the current cell [self updateDatePicker]; } #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; respectiveOptions = [[NSArray alloc]init]; respectiveOptions = [ASOR objectAtIndex:indexPath.row]; if ([kDateCellID isEqualToString:cell.reuseIdentifier]) { [self displayInlineDatePickerForRowAtIndexPath:indexPath]; } else { [tableView deselectRowAtIndexPath:indexPath animated:YES]; } } - (UIPickerView*)pickerViewForCell:(UITableViewCell*)cell { UIPickerView * picker = [[UIPickerView alloc] initWithFrame:cell.bounds]; [picker setDelegate:self]; [picker setDataSource:self]; picker.hidden = YES; [cell addSubview:picker]; return picker; } #pragma mark - Actions /*! User chose to change the date by changing the values inside the UIDatePicker. @param sender The sender for this action: UIDatePicker. */ //- (void)dateAction:(id)sender //{ // NSIndexPath *targetedCellIndexPath = nil; // // if ([self hasInlineDatePicker]) // { // // inline date picker: update the cell's date "above" the date picker cell // // // targetedCellIndexPath = [NSIndexPath indexPathForRow:self.datePickerIndexPath.row - 1 inSection:0]; // } // else // { // // external date picker: update the current "selected" cell's date // targetedCellIndexPath = [self.tableView indexPathForSelectedRow]; // } // // UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:targetedCellIndexPath]; // UIDatePicker *targetedDatePicker = sender; // // // update our data model // NSMutableDictionary *itemData = self.dataArray[targetedCellIndexPath.row]; // [itemData setValue:targetedDatePicker.date forKey:kDateKey]; // // // update the cell's date string // cell.detailTextLabel.text = [self.dateFormatter stringFromDate:targetedDatePicker.date]; //} // Number of components. -(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{ return 1; } // Total rows in our component. -(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ return [respectiveOptions count]; } // Display each row's data. -(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{ return [respectiveOptions objectAtIndex:row]; } // Do something with the selected row. -(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{ }