iOS 7 - How to display a date picker in place in a table view?

87,087

Solution 1

With iOS7, Apple released the sample code DateCell.

enter image description here

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.

enter image description here

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);
}
Share:
87,087
X.Y.
Author by

X.Y.

Updated on March 14, 2020

Comments

  • X.Y.
    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:

    In-place date picker

  • Aaron Bratcher
    Aaron Bratcher over 10 years
    Apple's code has problems. See my answer below if you want to keep your static tableView
  • Nitin Gohel
    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
    Chris Garrett over 10 years
    This 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
    ICL1901 over 10 years
    Has anyone used the sample to create a text picker? if so, how? Thanks
  • Aaron Bratcher
    Aaron Bratcher over 10 years
    You 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
    mblackwell8 over 10 years
    This 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
    EmilDo over 10 years
    I used the apple example but Aarons' example is much better and easy to implement.
  • DevC
    DevC over 10 years
    is there something else missing here? The height of the cells arent changing :)
  • DevC
    DevC over 10 years
    Since iOS 7.1 you must also select "Clip to SubViews" in the Attributes inspector - thats what I was missing :)
  • EmilDo
    EmilDo over 10 years
    There 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
    Aaron Bratcher over 10 years
    I just noticed that bug and was about to look at a workaround. Thanks!
  • Federer
    Federer over 10 years
    Thanks guys, great job.
  • budiDino
    budiDino over 10 years
    Overwrite the UIView animate block with this: [tableView beginUpdates]; [tableView endUpdates]; // simpler solution
  • Mughees Musaddiq
    Mughees Musaddiq about 10 years
    Thanks @Ajay, I used your amended code and want to replace UIDatePicker with UIPickerView. I tried few things and able to show pickerView 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
    Ajay Gautam about 10 years
    I 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
    Mughees Musaddiq about 10 years
    Thanks, I somehow managed to replace UIDatePicker with UIPickerView but with couple of bugs. 1. It crashes when you open UIPickerView and scroll the table. 2. It automatically assigns the value to UILabel 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
    Mughees Musaddiq about 10 years
    Are you able to find the testProject, Any luck??
  • Ajay Gautam
    Ajay Gautam about 10 years
    Sorry, I got a bit busy. Still need help with this?
  • Mughees Musaddiq
    Mughees Musaddiq about 10 years
    Yes 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
    Peter Johnson about 10 years
    Also, the line "self.dateOpen = !self.isDateOpen;" should read "self.isDateOpen =" at the start, not "self.dateOpen ="
  • Barry Haanstra
    Barry Haanstra about 10 years
    The [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
    Aaron Bratcher almost 10 years
    I tried the beginUpdates / endUpdates calls in place of the UIView animateWithDuration and found it isn't reliable.
  • Anirudha Agashe
    Anirudha Agashe almost 10 years
    most horrible sample code. They could do with some advice on when to create a method from Code Complete
  • Isuru
    Isuru almost 10 years
    Thank you. Works in iOS 8 too.
  • testing
    testing over 9 years
    If 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
    Josh over 9 years
    Thanks @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
    Timothy Logan over 9 years
    You're right. In fact, I had fixed the issue but stack overflow wouldn't let me edit the code I put there.
  • nh32rg
    nh32rg over 9 years
    I 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
    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
    Dunken about 9 years
    Hi see the "collapsed" cell in the background... Why is this? Are all the cells transparent by default?
  • Chris Nolet
    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
    Daniel over 8 years
    Wow 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
    Terrance Shaw over 8 years
    This 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
    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
    Nicolas Miari about 8 years
    Why the 219 constant for the height? Shouldn't it be 216? (default height of a date picker)
  • Nicolas Miari
    Nicolas Miari about 8 years
    This 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