iOS 7 – 如何在表格视图中显示dateselect器?
在WWDC 2013video中,Applebuild议在iOS 7的桌面视图中显示select器。如何在表格视图单元格之间插入视图并为其设置animation效果?
像这样,从苹果日历应用程序:
在iOS7中,Apple发布了DateCell
示例代码。
演示格式化显示表单元格中的date对象,并使用UIDatePicker编辑这些值。 作为此表的委托,示例使用方法“didSelectRowAtIndexPath”打开UIDatePicker控件。
对于iOS 6.x及更早版本,UIViewAnimation用于将UIDatePicker向上和向下滑动到屏幕上。 对于iOS 7.x,将UIDatePicker内嵌添加到表视图。
UIDatePicker的操作方法将直接设置自定义表单元格的NSDate属性。 另外,这个示例演示了如何使用NSDateFormatter类来实现自定义单元格的date格式外观。
你可以在这里下载示例代码: DateCell 。
你可以使用我之前给出的答案,或者在Swift中使用这个新类来使这个任务变得更简单和更简洁: https : //github.com/AaronBratcher/TableViewHelper
我发现苹果提供的代码在几个方面有问题:
- 你不能有一个静态tableView,因为他们正在使用tableView:cellForRowAtIndexPath方法
- 如果在最后一个dateselect器单元格下没有额外的行,代码将崩溃
对于静态单元格表格,我在我的date显示单元格下面定义了我的date选取器单元格,并有一个标识标识了我是否正在编辑它。 如果我是,我返回一个适当的单元格高度,否则我返回一个单元格高度为零。
- (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; } }
当显示date的行被点击时,我更改标志并执行更新animation来显示select器。
-(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]; }]; } }
如果我在同一个表中有多个date/时间select器,则在点击时相应地设置标记并重新加载适当的行。 我发现我可以保留我的静态表,使用更less的代码,并且更容易理解正在发生的事情。
使用故事板和静态表,我能够使用下面的代码实现相同的结果。 这是一个很好的解决scheme,因为如果你有很多形状奇怪的单元格,或者想要多个dynamic显示/隐藏的单元格,这个代码仍然可以工作。
@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]; }
我已经从苹果的DateCell来源,并删除故事板文件。
如果你想要一个没有故事板,请看看: https : //github.com/ajaygautam/DateCellWithoutStoryboard
关于这个最好的教程之一是iOS 7内联UIDatePicker – 第2部分 。 基本上我在这里使用静态表格视图单元格和实现一些额外的方法。 我用这个Xamarin和C#
你必须激活Clip Subviews
。
设置高度:
public override float GetHeightForRow (UITableView tableView, NSIndexPath indexPath) { if (indexPath.Row == 4) { return (datePickerIsShowing) ? 206f : 0.0f; } return base.GetHeightForRow(tableView,indexPath); }
比类variables: private bool datePickerIsShowing = false;
显示dateselect器:
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; } ); }
隐藏dateselect器:
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); }
我已经做了我自己的自定义视图控制器来简化在tableview中内联添加内联select器的过程。 您只需将其子类化并按照一些简单的规则处理dateselect器演示文稿。
你可以在这里find一个演示如何使用它的示例项目: https : //github.com/ale84/ALEInlineDatePickerViewController
Aaron Bratcher的答案除了与多个部分一起使用之外。 animation有点波动,并没有把下一个部分放下来。 为了解决这个问题,我迭代了下一组节,并将这些行翻译成与dateselect器的高度相同的数量。
我编辑了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]; }]; }
除了之前的答案之外,
我试过@datinc和@Aaron Bratcher解决scheme,两者都很好,但animation在分组静态tableView中并不那么干净。
玩了一下后,我得到了这个代码,工程干净,伟大的我 –
- (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];
更新行,这样的表格部分和单元格的其余部分不是animation。
希望它可以帮助别人。
沙尼
我发现苹果的date单元格中的一个缺陷的答案,您必须有一行低于最后一个date单元格或您遇到错误。 在CellForRowAtIndexPath方法中,将ItemData行replace为
NSArray *itemsArray = [self.dataArray objectAtIndex:indexPath.section]; NSDictionary *itemData = nil; if(![indexPath isEqual:self.datePickerIndexPath]) itemData = [itemsArray objectAtIndex:modelRow];
replace示例代码后,我现在可以显示一个datePicker单元格,而不在其下面有一个单元格。
我只是join了stackoverflow,所以如果这是在错误的地方或其他地方,我道歉。
添加到以前的答案和@Aaron Bratcher解决scheme…
自iOS9以来,我的animation越来越不稳定了,表格花了一段时间才装载,足以令人讨厌。 我缩小了对dateselect器缓慢加载的情节。 以编程方式而不是在故事板中添加采集器可以提高加载性能,而作为副产品,animation更加stream畅。
从故事板中删除dateselect器,并有一个空的单元格,您可以在之前的答案中设置高度,然后在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]; }
然后执行行动,例如
- (IBAction)startTimePickerChanged:(id)sender { NSLog(@"start time picker changed"); }
这加载表比以前更快。 您还从didSelectRowAtIndexPath
删除animation线,因为它没有它的animation(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; } }
在iOS 8.1中使用没有animation的答案可以正常工作。 我已经把它转换成Swift下面:
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() } }
这是解决问题的另一种方法,没有静态常数。 所有单元格都可以用于静态和dynamic表格视图。 此方法使用单个单元格为标题和dateselect器!
顺便说一句,你可以在你的桌子上有很多dateselect器!
创build一个UITableViewCell子类:
所有的表格视图单元格都必须从这个类inheritance ,并且您必须为每一行手动设置单元格高度。
// // CPTableViewCell.h // // Copyright (c) CodePigeon. All rights reserved. // @class CPTableViewCell; #define kUIAnimationDuration 0.33f @protocol CPTableViewCellDelegate <NSObject> @required - (void)tableViewCellDidChangeValue:(CPTableViewCell *)cell; @optional - (void)tableViewCellDidBecomeFirstResponder:(CPTableViewCell *)cell; - (void)tableViewCellResignedFirstResponder:(CPTableViewCell *)cell; @end @interface CPTableViewCell : UITableViewCell @property (nonatomic, weak) IBOutlet UITableView *tableView; @property (nonatomic, weak) IBOutlet CPTableViewCell *nextCell; @property (nonatomic, weak) IBOutlet id<CPTableViewCellDelegate> delegate; @property (nonatomic, copy) IBInspectable NSString *dataBindKey; @property (nonatomic) IBInspectable CGFloat height; @property (nonatomic, readonly) BOOL isFirstResponder; @property (nonatomic) BOOL isEnabled; - (void)commonInit; - (id)value; - (void)setValue:(id)value; @end // // CPTableViewCell.m // // Copyright (c) CodePigeon. All rights reserved. // #import "CPTableViewCell.h" @interface CPTableViewCell () @end @implementation CPTableViewCell - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (!self) return nil; [self commonInit]; return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (!self) return nil; [self commonInit]; return self; } - (void)commonInit { _isFirstResponder = NO; _isEnabled = YES; } - (BOOL)canBecomeFirstResponder { return _isEnabled; } - (BOOL)becomeFirstResponder { if ([_delegate respondsToSelector:@selector(tableViewCellDidBecomeFirstResponder:)]) [_delegate tableViewCellDidBecomeFirstResponder:self]; return _isFirstResponder = YES; } - (BOOL)resignFirstResponder { if (_isFirstResponder) { if ([_delegate respondsToSelector:@selector(tableViewCellResignedFirstResponder:)]) [_delegate tableViewCellResignedFirstResponder:self]; _isFirstResponder = NO; } return _isFirstResponder; } - (id)value { [self doesNotRecognizeSelector:_cmd]; return nil; } - (void)setValue:(id)value { [self doesNotRecognizeSelector:_cmd]; } @end
从我们的CPTableViewCell创build一个CPDatePickerTableViewCell类
// // CPDatePickerTableViewCell.h // // Copyright (c) CodePigeon. All rights reserved. // #import "CPTableViewCell.h" @interface CPDatePickerTableViewCell : CPTableViewCell @property (nonatomic, copy) IBInspectable NSString *dateFormat; @property (nonatomic, weak) IBOutlet UILabel *titleLabel; @property (nonatomic, weak) IBOutlet UILabel *dateLabel; @property (nonatomic, weak) IBOutlet UIDatePicker *datePicker; @end // // CPDatePickerTableViewCell.m // // Copyright (c) CodePigeon. All rights reserved. // #import "CPDatePickerTableViewCell.h" #define kCPDatePickerTableViewCellPickerHeight 162.f @interface CPDatePickerTableViewCell () <UITextFieldDelegate, UIPickerViewDelegate> { NSDateFormatter *_dateFormatter; BOOL _isOpen; } @end @implementation CPDatePickerTableViewCell - (void)awakeFromNib { [super awakeFromNib]; _dateFormatter = [NSDateFormatter new]; [_dateFormatter setDateFormat:_dateFormat]; self.selectionStyle = UITableViewCellSelectionStyleNone; _dateLabel.text = [_dateFormatter stringFromDate:_datePicker.date]; _datePicker.alpha = 0.f; _isOpen = NO; } - (BOOL)becomeFirstResponder { if (_isOpen == NO) { self.height += kCPDatePickerTableViewCellPickerHeight; } else { self.height -= kCPDatePickerTableViewCellPickerHeight; } [UIView animateWithDuration:kUIAnimationDuration animations:^{ _datePicker.alpha = _isOpen ? 0.0f : 1.0f; }]; [self.tableView beginUpdates]; [self.tableView endUpdates]; _isOpen = !_isOpen; [self.tableView endEditing:YES]; return [super becomeFirstResponder]; } - (BOOL)resignFirstResponder { if (_isOpen == YES) { self.height -= kCPDatePickerTableViewCellPickerHeight; [UIView animateWithDuration:kUIAnimationDuration animations:^{ _datePicker.alpha = 0.0f; }]; [self.tableView beginUpdates]; [self.tableView endUpdates]; _isOpen = NO; } return [super resignFirstResponder]; } - (id)value { return _datePicker.date; } - (void)setValue:(NSDate *)value { _datePicker.date = value; _dateLabel.text = [_dateFormatter stringFromDate:_datePicker.date]; } - (IBAction)datePickerValueChanged:(UIDatePicker *)sender { [_dateLabel setText:[_dateFormatter stringFromDate:_datePicker.date]]; [self.delegate tableViewCellDidChangeValue:self]; } @end
在你的视图控制器中实现这两个委托方法
#pragma mark - UITableViewDelegate methods - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { CPTableViewCell *cell = (CPTableViewCell *)[super tableView:tableView cellForRowAtIndexPath:indexPath]; return [cell height]; } - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath { CPTableViewCell *cell = (CPTableViewCell *)[tableView cellForRowAtIndexPath:indexPath]; if ([cell canBecomeFirstResponder]) { [cell becomeFirstResponder]; } if (cell != _selectedCell) { [_selectedCell resignFirstResponder]; } _selectedCell = cell; return YES; }
示例如何在“界面”构build器中设置约束
此外,我已经编写了UITextField和UITextView的自定义单元格类,其中tableView:didSelectRowAtIndexPath:select单元格时调用!
CPTextFieldTableViewCell
// // CPTextFieldTableViewCell.h // // Copyright (c) CodePigeon. All rights reserved. // #import "CPTableViewCell.h" @interface CPTextFieldTableViewCell : CPTableViewCell @property (nonatomic, weak) IBOutlet UITextField *inputTextField; @end // // CPTextFieldTableViewCell.m // // Copyright (c) CodePigeon. All rights reserved. // #import "CPTextFieldTableViewCell.h" @interface CPTextFieldTableViewCell () <UITextFieldDelegate> @end @implementation CPTextFieldTableViewCell - (void)awakeFromNib { [super awakeFromNib]; self.selectionStyle = UITableViewCellSelectionStyleNone; _inputTextField.userInteractionEnabled = NO; _inputTextField.delegate = self; } - (BOOL)becomeFirstResponder { _inputTextField.userInteractionEnabled = YES; [_inputTextField becomeFirstResponder]; return [super becomeFirstResponder]; } - (BOOL)resignFirstResponder { _inputTextField.userInteractionEnabled = NO; return [super resignFirstResponder]; } - (void)setIsEnabled:(BOOL)isEnabled { [super setIsEnabled:isEnabled]; _inputTextField.enabled = isEnabled; } - (id)value { return _inputTextField.text; } - (void)setValue:(NSString *)value { _inputTextField.text = value; } #pragma mark - UITextFieldDelegate methods - (void)textFieldDidEndEditing:(UITextField *)textField { [self.delegate tableViewCellDidChangeValue:self]; } @end
CBTextViewTableViewCell
单元格高度是dynamic的,当文本被换行时,行会增长!
// // CBTextViewTableViewCell.h // // Copyright (c) CodePigeon. All rights reserved. // #import "CPTableViewCell.h" @interface CPTextViewTableViewCell : CPTableViewCell @property (nonatomic, weak) IBOutlet UITextView *inputTextView; @end // // CBTextViewTableViewCell.m // // Copyright (c) CodePigeon. All rights reserved. // #import "CPTextViewTableViewCell.h" @interface CPTextViewTableViewCell () <UITextViewDelegate> { UITextView *_heightTextView; } @end @implementation CPTextViewTableViewCell @synthesize height = _height; - (void)awakeFromNib { [super awakeFromNib]; self.selectionStyle = UITableViewCellSelectionStyleNone; _inputTextView.userInteractionEnabled = NO; _inputTextView.delegate = self; _inputTextView.contentInset = UIEdgeInsetsZero; _inputTextView.scrollEnabled = NO; } - (CGFloat)height { if (!_heightTextView) { CGRect frame = (CGRect) { .origin = CGPointMake(0.f, 0.f), .size = CGSizeMake(_inputTextView.textInputView.frame.size.width, 0.f) }; _heightTextView = [[UITextView alloc] initWithFrame:frame]; _heightTextView.font = [UIFont systemFontOfSize:_inputTextView.font.pointSize]; _heightTextView.textColor = UIColor.whiteColor; _heightTextView.contentInset = UIEdgeInsetsZero; } _heightTextView.text = _inputTextView.text; CGSize size = [_heightTextView sizeThatFits:CGSizeMake(_inputTextView.textInputView.frame.size.width, FLT_MAX)]; return size.height > _height ? size.height + _inputTextView.font.pointSize : _height; } - (BOOL)becomeFirstResponder { _inputTextView.userInteractionEnabled = YES; [_inputTextView becomeFirstResponder]; return [super becomeFirstResponder]; } - (BOOL)resignFirstResponder { _inputTextView.userInteractionEnabled = NO; return [super resignFirstResponder]; } - (void)setIsEnabled:(BOOL)isEnabled { [super setIsEnabled:isEnabled]; _inputTextView.editable = isEnabled; } - (id)value { return _inputTextView.text; } - (void)setValue:(NSString *)value { _inputTextView.text = value; [_inputTextView setNeedsLayout]; [_inputTextView layoutIfNeeded]; } #pragma mark - UITextViewDelegate methods - (void)textViewDidChange:(UITextView *)textView { [self.delegate tableViewCellDidChangeValue:self]; [self.tableView beginUpdates]; [self.tableView endUpdates]; } @end
在Swift版本中使用DateCell最简单的方法:使用这个例子 。
- 打开这个例子并testing它(使Xcode转换成Swift 2)
-
将“ DateCellTableViewController.swift ”类拖到您的项目中。
-
打开“Main.storyboard”并复制“ DateCell ”ViewController对象,并将其放在故事板中。