在WWDC 2013视频中,Apple建议在iOS 7的表格视图中就位显示选择器。如何在表格视图单元格之间插入视图并为其设置动画?
像这样,通过Apple日历应用程序:
在WWDC 2013视频中,Apple建议在iOS 7的表格视图中就位显示选择器。如何在表格视图单元格之间插入视图并为其设置动画?
像这样,通过Apple日历应用程序:
Answers:
在iOS7中,Apple发布了示例代码DateCell
。
演示表格单元格中日期对象的格式化显示以及如何使用UIDatePicker编辑这些值。作为此表的委托,该示例使用方法“ didSelectRowAtIndexPath”打开UIDatePicker控件。
对于iOS 6.x和更早版本,UIViewAnimation用于在屏幕上和屏幕外上下滑动UIDatePicker。对于iOS 7.x,将UIDatePicker内联添加到表格视图中。
UIDatePicker的操作方法将直接设置自定义表单元格的NSDate属性。此外,此示例演示如何使用NSDateFormatter类来实现自定义单元格的日期格式外观。
您可以在此处下载示例代码:DateCell。
您可以使用下面我以前给出的答案或使用斯威夫特我这个新类做,使这个任务很多简单和清晰:https://github.com/AaronBratcher/TableViewHelper
我发现Apple提供的代码在两种方面存在问题:
对于静态单元格表,我在日期显示单元格下面定义我的日期选择器单元格,并有一个标志来标识是否正在编辑它。如果是,则返回适当的像元高度,否则返回零的像元高度。
- (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];
}
[tableView reloadData];
不需要调用。当前,它会禁用行选择,但是最好取消选择这样的行:[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
我已经从Apple取得了DateCell的源,并删除了故事板文件。
如果您想要一个没有情节提要的人,请查看:https : //github.com/ajaygautam/DateCellWithoutStoryboard
UIDatePicker
为UIPickerView
。我尝试了一些事情,并且能够pickerView
在每个单元格上显示,但是并没有随每个单元格进行更新。我已经在这里发布了我的问题和代码。请看一下,如果您可以向我建议解决方案,那将非常好。
UIDatePicker
了UIPickerView
但有一些错误。1.当您打开UIPickerView
并滚动表格时,它会崩溃。2. UILabel
当将值分配给顶部行的“详细信息”标签时,它将自动将值分配给表底部的“详细信息”。这是我的代码
关于此的最佳教程之一是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);
}
比一个类变量: private bool datePickerIsShowing = false;
显示日期选择器:
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);
}
我制作了自己的自定义视图控制器,以简化在表视图中内联添加内联选择器的过程。您只需对其进行子类化,并遵循一些简单的规则,即可处理日期选择器演示文稿。
您可以在这里找到它以及演示如何使用它的示例项目:https : //github.com/ale84/ALEInlineDatePickerViewController
我在苹果的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];
}];
}
除了以前的答案,
我同时尝试了@datinc和@Aaron Bratcher解决方案,两者都很好用,但是动画在分组的静态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];
要更新该行,这样表格的其余部分和单元格不会设置动画。
希望它可以帮助某人。
香妮
添加到以前的答案和@Aaron Bratcher解决方案...
自从iOS 9起,我就获得了动荡的动画,并且该表格需要花费一些时间来加载,而且足够令人讨厌。我把它的范围缩小到从情节提要中加载日期选择器的速度很慢。以编程方式而不是在情节提要中添加选择器可以改善加载性能,并且作为副产品,动画更流畅。
从情节提要中删除日期选择器,并有一个空单元格,您可以像以前的答案中那样设置高度,然后在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
因为动画线没有动画线(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中正确使用不带动画的答案。我已经将其转换为下面的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()
}
}
这是没有静态常数的情况下解决问题的另一种方法。所有单元格都可以在静态和动态表视图中使用。此方法将单个单元格用于标题和日期选择器!
顺便说一句,您可以在表中随心所欲地选择日期选择器!
创建一个UITableViewCell子类:
您必须从此类继承所有表格视图单元格,并且必须手动为每一行设置单元格高度。
//
// 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 创建一个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;
}
示例如何在“接口”构建器中设置约束
另外,我为UITextField和UITextView编写了自定义单元格类,其中tableView:didSelectRowAtIndexPath:选择单元格时会调用
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
单元格高度是动态的,当文本换行时,行会增加!
//
// 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的最简单方法:使用以下示例。
将“ DateCellTableViewController.swift ”类拖到您的项目中。
打开“ Main.storyboard”,然后复制“ DateCell ” ViewController对象,并将其粘贴到情节提要中。