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