iOS 7 - How to display a date picker in place in a

2019-01-03 03:34发布

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

13条回答
乱世女痞
2楼-- · 2019-01-03 04:27

The answer from Aaron Bratcher worked except when used with multiple sections. The animations were a bit choppy and it didn't slide the next sections down very well. To fix this I iterated through the next set of sections and translated the rows down the same amount as the date picker's height.

I edited the didSelectRowAtIndexPath to:

// Return Data to delegate: either way is fine, although passing back the object may be more efficient
// [_delegate imageSelected:currentRecord.image withTitle:currentRecord.title withCreator:currentRecord.creator];
// [_delegate animalSelected:currentRecord];
if (indexPath.section == 1 && indexPath.row == 0) { // this is my date cell above the picker cell
    editingStartTime = !editingStartTime;
    [UIView animateWithDuration:.4 animations:^{
        int height = 0;
        if (editingStartTime) {
            height = 162;
        }
        UITableViewCell* temp = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]];
        [temp setFrame:CGRectMake(temp.frame.origin.x, temp.frame.origin.y, temp.frame.size.width, height)];
        for (int x = 2; x < [tableView numberOfSections]; x++) {
            for (int y = 0; y < [tableView numberOfRowsInSection:x]; y++) {
                UITableViewCell* temp = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:y inSection:x]];
                int y_coord = temp.frame.origin.y-162;
                if (editingStartTime) {
                    y_coord = temp.frame.origin.y+162;
                }
                [temp setFrame:CGRectMake(temp.frame.origin.x, y_coord, temp.frame.size.width, temp.frame.size.height)];
            }
        }
    }completion:^(BOOL finished){
        [self.tableView reloadData];
    }];
}
查看更多
三岁会撩人
3楼-- · 2019-01-03 04:29

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

查看更多
We Are One
4楼-- · 2019-01-03 04:30

Adding to the previous answers and @Aaron Bratcher solution...

I was getting choppy animations since iOS 9, and the table was taking a while to load, and enough to be annoying. I narrowed it do to the date pickers being slow to load from the storyboard. Adding the pickers programmatically rather than in the storyboard improved the loading performance, and as a by-product, the animation is smoother.

Remove the date picker from storyboard and have an empty cell, which you set the height as in previous answers, and then call an initialise on viewDidLoad:

- (void)initialiseDatePickers
{
    self.isEditingStartTime = NO;
    self.startTimePickerCell.clipsToBounds = YES;

    UIDatePicker *startTimePicker = [[UIDatePicker alloc] init];
    [startTimePicker addTarget:self action:@selector(startTimePickerChanged:) forControlEvents:UIControlEventValueChanged];
    [self.startTimePickerCell addSubview:startTimePicker];
}

Then implement the action e.g.

- (IBAction)startTimePickerChanged:(id)sender
{
    NSLog(@"start time picker changed");
}

This loads the table much faster than previously. You also remove the animation line from didSelectRowAtIndexPath as it animates smoothly without it (ymmv).

-(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;
    }
}
查看更多
一纸荒年 Trace。
5楼-- · 2019-01-03 04:33

Adding to the previous answers,

I tried both @datinc and @Aaron Bratcher solutions, both worked great but the animation was not so clean in a grouped static tableView.

After playing with it a little bit I got to this code that works clean and great for me -

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0 && indexPath.row == 1)
    {
        if (self.isPickerOpened)
        {
            return 162;
        }
        else
        {
            return 0;
        }
    }
    else
    {
        return [super tableView:tableView heightForRowAtIndexPath:indexPath];
    }
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.section == 0 && indexPath.row == 0) {
        [tableView beginUpdates];
        self.isPickerOpened = ! self.isPickerOpened;
        [super tableView:tableView heightForRowAtIndexPath:indexPath];
        [self.tableView endUpdates];
    }
}

The main change is to use -

        [super tableView:tableView heightForRowAtIndexPath:indexPath];

to update the row, this way the rest of the table sections and cells are not animating.

Hope it helps someone.

Shani

查看更多
【Aperson】
6楼-- · 2019-01-03 04:33

Using this answer without the animation works correctly in iOS 8.1. I have converted it into Swift below:

import UIKit

class TableViewController: UITableViewController {

    var editingCell: Bool = false

    @IBOutlet weak var myCell: UITableViewCell!

    override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {

        // Change the section and row to the title cell the user taps to reveal 
        // the cell below
        if (indexPath.section == 0 && indexPath.row == 2 && !editingCell) {
            return 0
        } else {
            return self.tableView.rowHeight
        }
    }

    override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

        self.tableView.deselectRowAtIndexPath(indexPath, animated: false);

        var cell = tableView.cellForRowAtIndexPath(indexPath)

        self.tableView.beginUpdates()
        if (cell == self.myCell) {
            editingType = !editingType;
        }
        self.tableView.endUpdates()
    }
}
查看更多
Bombasti
7楼-- · 2019-01-03 04:40

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.

查看更多
登录 后发表回答