Uitableview iOS 7-如何在表视图中就地显示日期选择器?

Uitableview iOS 7-如何在表视图中就地显示日期选择器?,uitableview,swift,ios7,uidatepicker,datecell,Uitableview,Swift,Ios7,Uidatepicker,Datecell,在WWDC 2013视频中,苹果建议在iOS 7的表格视图中显示选择器。如何在表格视图单元格之间插入视图并设置其动画 如下所示,从Apple日历应用程序: 通过iOS7,苹果发布了示例代码DateCell 演示表格单元格中日期对象的格式化显示,以及使用UIDatePicker编辑这些值。 作为此表的委托,示例使用方法“DidSelectRowatineXpath”打开UIDatePicker控件 对于iOS 6.x及更早版本,UIViewAnimation用于在屏幕上上上下滑动UIDatePi

在WWDC 2013视频中,苹果建议在iOS 7的表格视图中显示选择器。如何在表格视图单元格之间插入视图并设置其动画

如下所示,从Apple日历应用程序:


通过iOS7,苹果发布了示例代码
DateCell

演示表格单元格中日期对象的格式化显示,以及使用UIDatePicker编辑这些值。 作为此表的委托,示例使用方法“DidSelectRowatineXpath”打开UIDatePicker控件

对于iOS 6.x及更早版本,UIViewAnimation用于在屏幕上上上下滑动UIDatePicker。对于iOS 7.x,UIDatePicker被在线添加到表视图中

UIDatePicker的操作方法将直接设置自定义表单元格的NSDate属性。此外,此示例还显示了如何使用NSDateFormatter类实现自定义单元格的日期格式外观


您可以在此处下载示例代码:。

您可以使用我之前给出的以下答案,或者使用我在Swift中创建的新类,使此任务更简单、更清晰:


我发现苹果提供的代码在以下几个方面存在问题:

  • 不能使用静态tableView,因为它们使用的是tableView:CellForRowatineXpath方法
  • 如果在最后一个日期选择器单元格下没有其他行,代码将崩溃
对于静态单元格表,我在“日期显示”单元格下面定义了“日期选择器”单元格,并用一个标志标识是否正在编辑它。如果是,则返回适当的单元格高度,否则返回0的单元格高度

- (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;
    }
}
单击显示日期的行时,我更改标志并执行更新动画以显示选择器

-(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];
        }];
    }
}

如果在同一个表中有多个日期/时间选择器,我会相应地在单击上设置标志并重新加载相应的行。我发现我可以保留我的静态表,使用更少的代码,并且更容易理解正在发生的事情。

使用故事板和静态表,我能够使用以下代码实现相同的结果。这是一个很好的解决方案,因为如果您有许多形状奇怪的单元格,或者希望有多个动态显示/隐藏的单元格,那么这段代码仍然有效

@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];
}

我已经从Apple获取了DateCell源代码,并删除了脚本文件

如果您想要一个没有故事板的,请查看:

我制作了自己的自定义视图控制器,以简化在tableview中内联添加内联选择器的过程。您只需将其子类化并遵循一些简单的规则,它就可以处理日期选择器表示

您可以在这里找到它,以及演示如何使用它的示例项目:

我找到了苹果datecell示例中一个缺陷的答案,其中必须在最后一个datecell下面有一行,否则会出现错误。在CellForRowAtIndexPath方法中,将ItemData行替换为

NSArray *itemsArray = [self.dataArray objectAtIndex:indexPath.section];
NSDictionary *itemData = nil;

if(![indexPath isEqual:self.datePickerIndexPath])
    itemData = [itemsArray objectAtIndex:modelRow];
在替换了示例代码之后,我现在可以显示一个datePicker单元格,而不必在其下面显示单元格


我刚刚加入了stackoverflow,所以如果这是在错误的地方或其他地方,我道歉。

Aaron Bratcher的回答有效,除非与多个部分一起使用。动画有点起伏,接下来的部分没有滑得很好。为了解决这个问题,我迭代了下一组部分,并将行向下转换为与日期选择器高度相同的数量

我将didSelectRowAtIndexPath编辑为:

// 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];
    }];
}
//将数据返回给委托:任何一种方法都可以,尽管传回对象可能更有效
//[_所选代表图像:currentRecord.image with title:currentRecord.title with creator:currentRecord.creator];
//[_代表动物选择:currentRecord];
如果(indexPath.section==1&&indexPath.row==0){//这是选择器单元格上方的我的日期单元格
editingStartTime=!editingStartTime;
[UIView animateWithDuration:.4个动画:^{
整数高度=0;
如果(编辑开始时间){
高度=162;
}
UITableViewCell*temp=[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1第1节:1]];
[temp setFrame:CGRectMake(temp.frame.origin.x、temp.frame.origin.y、temp.frame.size.width、height)];
对于(int x=2;x<[tableView numberOfSections];x++){
对于(int y=0;y<[表视图行数部分:x];y++){
UITableViewCell*temp=[tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:y目录:x]];
int y_坐标=温度框架原点y-162;
如果(编辑开始时间){
y_坐标=温度帧原点y+162;
}
[温度设置框:CGRectMake(温度框原点x、y坐标、温度框尺寸宽度、温度框尺寸高度)];
}
}
}完成:^(布尔完成){
[self.tableView重载数据];
}];
}

这方面最好的教程之一是。基本上,这里我使用静态表视图单元格并实现一些附加方法。我用Xamarin和C#来做这个:

您必须激活
剪辑子视图

设置高度:

public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
{
    if (indexPath.Row == 4) {
        return (datePickerIsShowing) ? 206f : 0.0f;
    }

    return base.GetHeightForRow(tableView,indexPath);
}
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;
        }
    );
} 
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;
        }
    );
} 
而不是类变量:
private bool datePickerIsShowing=false

显示日期选择器:

public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
{
    if (indexPath.Row == 4) {
        return (datePickerIsShowing) ? 206f : 0.0f;
    }

    return base.GetHeightForRow(tableView,indexPath);
}
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;
        }
    );
} 
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;
        }
    );
} 
隐藏日期选择器:

public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath)
{
    if (indexPath.Row == 4) {
        return (datePickerIsShowing) ? 206f : 0.0f;
    }

    return base.GetHeightForRow(tableView,indexPath);
}
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;
        }
    );
} 
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;
        }
    );
} 
并调用此函数:

public override void RowSelected (UITableView tableView, NSIndexPath indexPath)
{
    if (indexPath.Row == 3) {
        if (datePickerIsShowing) {
            hideDatePickerCell ();
        } else {
            showDatePickerCell ();
        }
    }

    this.TableView.DeselectRow (indexPath, true);
}

除了前面的答案之外

我尝试了@datinc和@Aaron Bratcher两种解决方案,它们都很有效,但在分组静态表视图中动画不是很清晰

在玩了一点之后,我得到了这段代码,它对我来说是干净和伟大的-

- (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];
    }
}
主要的变化是使用-

        [super tableView:tableView heightForRowAtIndexPath:indexPath];
若要更新行,请使用这种方式,表的其余部分和单元格不会设置动画

希望它能帮助别人

Shani

在iOS 8中不使用动画也能正常工作
- (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];
}
- (IBAction)startTimePickerChanged:(id)sender
{
    NSLog(@"start time picker changed");
}
-(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;
    }
}