iOS 7 - How to display a date picker in place in a table view?
With iOS7, Apple released the sample code DateCell
.
Demonstrates formatted display of date objects in table cells and use of UIDatePicker to edit those values. As a delegate to this table, the sample uses the method "didSelectRowAtIndexPath" to open the UIDatePicker control.
For iOS 6.x and earlier, UIViewAnimation is used for sliding the UIDatePicker up on-screen and down off-screen. For iOS 7.x, the UIDatePicker is added in-line to the table view.
The action method of the UIDatePicker will directly set the NSDate property of the custom table cell. In addition, this sample shows how to use NSDateFormatter class to achieve the custom cell's date-formatted appearance.
You can download the sample code here: DateCell.
You can use the answer I had previously given below or use this new class in Swift I made to make this task a lot simpler and cleaner: https://github.com/AaronBratcher/TableViewHelper
I find the code provided by Apple to be problematic in a couple of ways:
- You can't have a static tableView because they are using the tableView:cellForRowAtIndexPath method
- The code crashes if you don't have additional rows below the last date picker cell
For static cell tables, I define my date picker cell below my date display cell and have a flag identifying if I'm editing it. If I am, I return a cell height appropriate, otherwise I return a cell height of zero.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0 && indexPath.row == 2) { // this is my picker cell
if (editingStartTime) {
return 219;
} else {
return 0;
}
} else {
return self.tableView.rowHeight;
}
}
When the row showing the date is clicked, I change the flag and do the update animation to show the picker.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0 && indexPath.row == 1) { // this is my date cell above the picker cell
editingStartTime = !editingStartTime;
[UIView animateWithDuration:.4 animations:^{
[self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:2 inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
}];
}
}
If I have multiple date/time pickers in the same table, I set the flags accordingly on the click and reload the appropriate rows. I've found that I can keep my static table, use a lot less code, and it is easier to understand what is happening.
Using the storyboard and a static table I was able to achieve the same result using the following code. This is a great solution because if you have many oddly shaped cells or want to have multiple cells that are dynamically shown/hidden this code will still work.
@interface StaticTableViewController: UITableViewController
@property (weak, nonatomic) IBOutlet UITableViewCell *dateTitleCell; // cell that will open the date picker. This is linked from the story board
@property (nonatomic, assign, getter = isDateOpen) BOOL dateOpen;
@end
@implementation StaticTableViewController
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// This is the index path of the date picker cell in the static table
if (indexPath.section == 1 && indexPath.row == 1 && !self.isDateOpen){
return 0;
}
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
[tableView beginUpdates];
if (cell == self.dateTitleCell){
self.dateOpen = !self.isDateOpen;
}
[tableView reloadData];
[self.tableView endUpdates];
}