iOS 7 - How to display a date picker in place in a table view?
Solution 1
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.
Solution 2
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.
Solution 3
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];
}
Solution 4
I have taken the DateCell source from Apple, and removed the storyboard file.
If you want one without storyboard, take a look at: https://github.com/ajaygautam/DateCellWithoutStoryboard
Solution 5
One of the best tutorials about this is iOS 7 in-line UIDatePicker – Part 2. Basically here I use static table view cells and implement some additional methods. I used Xamarin and C# for this:
You have to active Clip Subviews
.
Setting the height:
public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
{
if (indexPath.Row == 4) {
return (datePickerIsShowing) ? 206f : 0.0f;
}
return base.GetHeightForRow(tableView,indexPath);
}
Than a class variable: private bool datePickerIsShowing = false;
Show date picker:
private void showDatePickerCell(){
datePickerIsShowing = true;
this.TableView.BeginUpdates ();
this.TableView.EndUpdates ();
this.datePicker.Hidden = false;
this.datePicker.Alpha = 0.0f;
UIView.Animate (0.25, animation:
() => {
this.datePicker.Alpha = 1.0f;
}
);
}
Hide date picker:
private void hideDatePickerCell(){
datePickerIsShowing = false;
this.TableView.BeginUpdates ();
this.TableView.EndUpdates ();
UIView.Animate (0.25,
animation: () => {
this.datePicker.Alpha = 0.0f;
},
completion: () => {
this.datePicker.Hidden = true;
}
);
}
And calling this functions:
public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
if (indexPath.Row == 3) {
if (datePickerIsShowing) {
hideDatePickerCell ();
} else {
showDatePickerCell ();
}
}
this.TableView.DeselectRow (indexPath, true);
}
X.Y.
Updated on March 14, 2020Comments
-
X.Y. over 4 years
In WWDC 2013 video, Apple suggests displaying picker in place in a table view in iOS 7. How to insert and animate a view between table view cells?
Like this, from the Apple calendar app:
-
Aaron Bratcher over 10 yearsApple's code has problems. See my answer below if you want to keep your static tableView
-
Nitin Gohel over 10 years@AaronBratcher i just introduce Datecell in my answer and you never expect you get exact code that you want. you have to modify and change as par you requirement. your solution is also good but remember you never get exact you what from other code that you requirement.
-
Chris Garrett over 10 yearsThis worked for me, except there appears to be a bug in UITableView. The row height was being returned correctly in my method, but it actually toggled the opposite height that I wanted. If I reloaded the rows twice it worked. (I didn't use tableView reloadData because I didn't want to reload the whole thing). Also the animation block wasn't necessary, it seems to animate on its own just fine.
-
ICL1901 over 10 yearsHas anyone used the sample to create a text picker? if so, how? Thanks
-
Aaron Bratcher over 10 yearsYou mean to reveal a row so the user can enter text instead of select a date? If so, the code is the same. Just the contents of the revealed row is different.
-
mblackwell8 over 10 yearsThis is a lot simpler (and thus much less error prone) IMHO than Apple's sample project, which inserts and deletes rows rather than showing and hiding. Thanks +1
-
EmilDo over 10 yearsI used the apple example but Aarons' example is much better and easy to implement.
-
DevC over 10 yearsis there something else missing here? The height of the cells arent changing :)
-
DevC over 10 yearsSince iOS 7.1 you must also select "Clip to SubViews" in the Attributes inspector - thats what I was missing :)
-
EmilDo over 10 yearsThere us a bug with iOS7.1 that shows objects out of the cell bounds - like the one with height 0 and picker inside. The solution is to get an outlet for the cell and set cell.clipsToBounds = YES;
-
Aaron Bratcher over 10 yearsI just noticed that bug and was about to look at a workaround. Thanks!
-
Federer over 10 yearsThanks guys, great job.
-
budiDino over 10 yearsOverwrite the UIView animate block with this: [tableView beginUpdates]; [tableView endUpdates]; // simpler solution
-
Mughees Musaddiq about 10 yearsThanks @Ajay, I used your amended code and want to replace
UIDatePicker
withUIPickerView
. I tried few things and able to showpickerView
on each cell but it doesn't gets updated with each cell. I have posted my question and code here. Please have a look and it would be very kind if you could suggest me a solution. -
Ajay Gautam about 10 yearsI saw your question... it has too much info, and doesn't really explain much. Perhaps you can upload your code / zipped up somewhere for me to take a look. I can debug it - if I can run the code...
-
Mughees Musaddiq about 10 yearsThanks, I somehow managed to replace
UIDatePicker
withUIPickerView
but with couple of bugs. 1. It crashes when you openUIPickerView
and scroll the table. 2. It automatically assigns the value toUILabel
Detail at the bottom rows of the table when values are being assigned to Details label on top rows. Here is my code -
Mughees Musaddiq about 10 yearsAre you able to find the testProject, Any luck??
-
Ajay Gautam about 10 yearsSorry, I got a bit busy. Still need help with this?
-
Mughees Musaddiq about 10 yearsYes please, If you could look at the bugs. I think there is a problem with index path. Please have a look Here is my code
-
Peter Johnson about 10 yearsAlso, the line "self.dateOpen = !self.isDateOpen;" should read "self.isDateOpen =" at the start, not "self.dateOpen ="
-
Barry Haanstra about 10 yearsThe
[tableView reloadData];
call is not required. Currently it disables the row selection, but it is better to deselect the row like this:[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
-
Aaron Bratcher almost 10 yearsI tried the beginUpdates / endUpdates calls in place of the UIView animateWithDuration and found it isn't reliable.
-
Anirudha Agashe almost 10 yearsmost horrible sample code. They could do with some advice on when to create a method from Code Complete
-
Isuru almost 10 yearsThank you. Works in iOS 8 too.
-
testing over 9 yearsIf you downvote, perhaps you can explain why you downvote. Instead of posting links to sites I also included some code. In my case it's C#. Don't know what's wrong with that.
-
Josh over 9 yearsThanks @Timmahh, I found the section animation choppy too. I used your solution successfully in my first Swift app! I found that cellForRowAtIndexPath returns nil where cells are off-screen though. To avoid this I added an IB outlet-collection containing all my cells, that came after the picker cell, and iterated through them with your new y_coord. This seems inflexible though as I have to keep the collection up-to-date when I add new cells.
-
Timothy Logan over 9 yearsYou're right. In fact, I had fixed the issue but stack overflow wouldn't let me edit the code I put there.
-
nh32rg over 9 yearsI had weird animation effects like the date cell would be blank after "opening" but using the begin and endupdates solved that. Nice and smooth now. Thanks datinc!
-
Vladimír Slavík over 9 years@Emilio What do you mean by getting the outlet? I set the cell.clipsToBounds = YES; in tableView:cellForRowAtIndexPath: Does not work for me. I had to set the cell.picker.hidden = ! editingStartTime;
-
Dunken about 9 yearsHi see the "collapsed" cell in the background... Why is this? Are all the cells transparent by default?
-
Chris Nolet about 9 years@Dunken - A solution is described in the up-voted comment above: stackoverflow.com/questions/18973573/… Even though the row has a height of zero, the contents aren't clipped by default, so you can see them underneath the following row.
-
Daniel over 8 yearsWow the Apple code is.... well, lets say the focus is on the table view effect. I hope no one gets inspired by their code in this sample project :s
-
Terrance Shaw over 8 yearsThis helped tremendously; thank you! Specifically the [super tableView] aspect, bundled with Aaron Bratcher's post, got me the results I needed on iOS 9.2. Thank you again!
-
Chris Shields over 8 years@AaronBratcher I realize this is an older thread. But I found your repository really helped me figure out how to get a smoothly animated Date Picker. I use Xamarin so I wrote a small port of your example to C# using the MonoTouch framework. github.com/CheezeCoder/AaronBratcher_TableViewHelper_Xamarin I hope it helps someone!
-
Nicolas Miari about 8 yearsWhy the 219 constant for the height? Shouldn't it be 216? (default height of a date picker)
-
Nicolas Miari about 8 yearsThis works for me if I omit the last
[self.tableView reloadData];
inside the animation block. If I leave it, the size change is instantaneous (animation is negated). - iOS 9