当你选择UITableViewCell时,你能动画一个高度变化吗?

我在我的iPhone应用程序中使用UITableView,并且我有一个属于一个组的人员列表。我希望当用户单击一个特定的人(因此选择单元格)时,单元格的高度会增加,以显示用于编辑这个人的属性的几个UI控件。

这可能吗?

137071 次浏览

获取所选行的indexpath。重新加载表。在UITableViewDelegate的highightforrowatindexpath方法中,将所选行的高度设置为不同的高度,对于其他的则返回正常的行高度

reloadData不好,因为没有动画…

这是我目前正在尝试的:

NSArray* paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView deleteRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];

它几乎是正确的。几乎。我正在增加单元格的高度,当单元格被替换时,有时在表格视图中会有一点“小问题”,就像在表格视图中保留了一些滚动位置一样,新单元格(表中的第一个单元格)的偏移量过高,而滚动视图会反弹以重新定位它。

我刚刚用一个小技巧解决了这个问题:

static int s_CellHeight = 30;
static int s_CellHeightEditing = 60;


- (void)onTimer {
cellHeight++;
[tableView reloadData];
if (cellHeight < s_CellHeightEditing)
heightAnimationTimer = [[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(onTimer) userInfo:nil repeats:NO] retain];
}


- (CGFloat)tableView:(UITableView *)_tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (isInEdit) {
return cellHeight;
}
cellHeight = s_CellHeight;
return s_CellHeight;
}

当我需要扩展单元格高度时,我设置isInEdit = YES并调用方法[self onTimer],它会动画单元格的增长,直到它达到s_cellhightediting值:-)

我发现了一个非常简单的解决方案,作为一个UITableView的副作用,我正在.....上工作

将单元格高度存储在一个变量中,该变量通常通过tableView: heightForRowAtIndexPath:来报告原始高度,然后当你想要动画化高度变化时,只需更改变量的值并调用此…

[tableView beginUpdates];
[tableView endUpdates];

你会发现它不会完全重载,但足以让UITableView知道它必须重绘单元格,为单元格....获取新的高度值你猜怎么着?它使你的改变变得生动。甜的。

我有一个更详细的解释和完整的代码示例在我的博客…动画UITableView单元格高度变化

我喜欢西蒙·李的回答。我实际上并没有尝试这个方法,但它看起来会改变列表中所有单元格的大小。我只是希望改变一下被窃听的细胞。我有点像西蒙,但有一点不同。这将改变选定单元格时的外观。它确实有动画。只是另一种方法。

创建一个int来保存当前选定单元格索引的值:

int currentSelection;

然后:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int row = [indexPath row];
selectedNumber = row;
[tableView beginUpdates];
[tableView endUpdates];
}

然后:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {


if ([indexPath row] == currentSelection) {
return  80;
}
else return 40;




}

我相信您可以在tableView:cellForRowAtIndexPath:中进行类似的更改,以更改单元格的类型,甚至为单元格加载xib文件。

像这样,currentSelection将从0开始。如果您不希望列表的第一个单元格(在索引0处)默认情况下看起来是选中的,则需要进行调整。

添加一个属性来跟踪选定的单元格

@property (nonatomic) int currentSelection;

将其设置为(例如)viewDidLoad中的哨兵值,以确保UITableView从“正常”位置开始

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.


//sentinel
self.currentSelection = -1;
}

heightForRowAtIndexPath中,你可以为选定的单元格设置你想要的高度

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
int rowHeight;
if ([indexPath row] == self.currentSelection) {
rowHeight = self.newCellHeight;
} else rowHeight = 57.0f;
return rowHeight;
}

didSelectRowAtIndexPath中,如果需要,保存当前选择并保存动态高度

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// do things with your cell here


// set selection
self.currentSelection = indexPath.row;
// save height for full text label
self.newCellHeight = cell.titleLbl.frame.size.height + cell.descriptionLbl.frame.size.height + 10;


// animate
[tableView beginUpdates];
[tableView endUpdates];
}
}

didDeselectRowAtIndexPath中,将选择索引设置回哨兵值,并将单元格动画回正常形式

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
// do things with your cell here


// sentinel
self.currentSelection = -1;


// animate
[tableView beginUpdates];
[tableView endUpdates];
}
}

我不知道连续调用beginUpdates/ endpdates是什么,你可以使用-[UITableView reloadRowsAtIndexPaths:withAnimation:]下面是一个示例项目

这是为了按索引展开行:

@property (nonatomic) NSIndexPath *expandIndexPath;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath
{
if ([indexPath isEqual:self.expandedIndexPath])
return 100;


return 44;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *modifiedRows = [NSMutableArray array];
if ([indexPath isEqual:self.expandIndexPath]) {
[modifiedRows addObject:self.expandIndexPath];
self.expandIndexPath = nil;
} else {
if (self.expandedIndexPath)
[modifiedRows addObject:self.expandIndexPath];


self.expandIndexPath = indexPath;
[modifiedRows addObject:indexPath];
}


// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];


// Preserve the deselection animation (if desired)
[tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ViewControllerCellReuseIdentifier];
cell.textLabel.text = [NSString stringWithFormat:@"I'm cell %ld:%ld", (long)indexPath.section, (long)indexPath.row];


return cell;
}

我用reloadRowsAtIndexPaths解决。

我在didSelectRowAtIndexPath中保存所选单元格的indexPath,并在最后调用reloadRowsAtIndexPaths(你可以发送NSMutableArray用于你想要重新加载的元素列表)。

heightForRowAtIndexPath中,你可以检查indexPath是否在expandIndexPath单元格的列表中,并发送高度。

你可以检查这个基本示例: https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam < a href = " https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam " > < / > 这是一个简单的解决方案

我加了一种代码来帮助你

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 20;
}


-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath*)indexPath
{
if ([indexPath isEqual:_expandIndexPath])
return 80;


return 40;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Celda";


UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];


[cell.textLabel setText:@"wopwop"];


return cell;
}


#pragma mark -
#pragma mark Tableview Delegate Methods


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSMutableArray *modifiedRows = [NSMutableArray array];
// Deselect cell
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
_expandIndexPath = indexPath;
[modifiedRows addObject:indexPath];


// This will animate updating the row sizes
[tableView reloadRowsAtIndexPaths:modifiedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}

只是一个提示,像我这样的人搜索添加“更多细节”自定义单元格。

[tableView beginUpdates];
[tableView endUpdates];
做了一个出色的工作,但不要忘记“裁剪”单元格视图。 从界面构建器中选择单元格->内容视图->从属性检查器中选择"剪辑子视图"

下面是我的自定义UITableView子类的代码,它在表单元格中展开UITextView,而无需重新加载(并失去键盘焦点):

- (void)textViewDidChange:(UITextView *)textView {
CGFloat textHeight = [textView sizeThatFits:CGSizeMake(self.width, MAXFLOAT)].height;
// Check, if text height changed
if (self.previousTextHeight != textHeight && self.previousTextHeight > 0) {
[self beginUpdates];


// Calculate difference in height
CGFloat difference = textHeight - self.previousTextHeight;


// Update currently editing cell's height
CGRect editingCellFrame = self.editingCell.frame;
editingCellFrame.size.height += difference;
self.editingCell.frame = editingCellFrame;


// Update UITableView contentSize
self.contentSize = CGSizeMake(self.contentSize.width, self.contentSize.height + difference);


// Scroll to bottom if cell is at the end of the table
if (self.editingNoteInEndOfTable) {
self.contentOffset = CGPointMake(self.contentOffset.x, self.contentOffset.y + difference);
} else {
// Update all next to editing cells
NSInteger editingCellIndex = [self.visibleCells indexOfObject:self.editingCell];
for (NSInteger i = editingCellIndex; i < self.visibleCells.count; i++) {
UITableViewCell *cell = self.visibleCells[i];
CGRect cellFrame = cell.frame;
cellFrame.origin.y += difference;
cell.frame = cellFrame;
}
}
[self endUpdates];
}
self.previousTextHeight = textHeight;
}

我使用了@Joy的精彩答案,它在ios 8.4和XCode 7.1.1上完美地工作。

如果你想让你的单元格可切换,我改变了-tableViewDidSelect如下:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//This is the bit I changed, so that if tapped once on the cell,
//cell is expanded. If tapped again on the same cell,
//cell is collapsed.
if (self.currentSelection==indexPath.row) {
self.currentSelection = -1;
}else{
self.currentSelection = indexPath.row;
}
// animate
[tableView beginUpdates];
[tableView endUpdates];


}

我希望这些对你有帮助。

在ios7及以上版本后检查此方法。

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}

iOS 8对此进行了改进。我们可以将它设置为表视图本身的属性。

西蒙·李的回答。

// MARK: - Variables
var isCcBccSelected = false // To toggle Bcc.






// MARK: UITableViewDelegate
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {


// Hide the Bcc Text Field , until CC gets focused in didSelectRowAtIndexPath()
if self.cellTypes[indexPath.row] == CellType.Bcc {
if (isCcBccSelected) {
return 44
} else {
return 0
}
}


return 44.0
}

然后在didSelectRowAtIndexPath()中

  func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.tableView.deselectRowAtIndexPath(indexPath, animated: true)


// To Get the Focus of CC, so that we can expand Bcc
if self.cellTypes[indexPath.row] == CellType.Cc {


if let cell = tableView.cellForRowAtIndexPath(indexPath) as? RecipientTableViewCell {


if cell.tag == 1 {
cell.recipientTypeLabel.text = "Cc:"
cell.recipientTextField.userInteractionEnabled = true
cell.recipientTextField.becomeFirstResponder()


isCcBccSelected = true


tableView.beginUpdates()
tableView.endUpdates()
}
}
}
}

是的,这是可能的。

UITableView有一个委托方法didSelectRowAtIndexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[UIView animateWithDuration:.6
delay:0
usingSpringWithDamping:UIViewAnimationOptionBeginFromCurrentState
initialSpringVelocity:0
options:UIViewAnimationOptionBeginFromCurrentState animations:^{


cellindex = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
NSArray* indexArray = [NSArray arrayWithObjects:indexPath, nil];
[violatedTableView beginUpdates];
[violatedTableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationAutomatic];
[violatedTableView endUpdates];
}
completion:^(BOOL finished) {
}];
}

但在你的情况下,如果用户滚动并选择了不同的单元格,那么你需要让最后选中的单元格收缩和扩展当前选中的单元格reloadRowsAtIndexPaths:调用heightForRowAtIndexPath:,因此相应地处理。

BOOL flag;


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
flag = !flag;
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationAutomatic];
[tableView endUpdates];
}


- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES == flag ? 20 : 40;
}

下面是Simons对Swift 3的简短回答。还允许切换单元格的选择

var cellIsSelected: IndexPath?




func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
cellIsSelected = cellIsSelected == indexPath ? nil : indexPath
tableView.beginUpdates()
tableView.endUpdates()
}




func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if cellIsSelected == indexPath {
return 250
}
return 65
}

西蒙·李的回答的Swift版本:

tableView.beginUpdates()
tableView.endUpdates()

请记住,你应该修改高度属性之前 endUpdates()

输入- - -

< p > < >强tableView.beginUpdates () tableView.endUpdates() < / >强 这些函数不会调用

func tableView(_ tableView: UITableView, cellForRowAt indexPath: indexPath) -> UITableViewCell {}

但是,如果你这样做, 表视图。reloadRows (:(selectedIndexPath !.none)

将调用 func tableView(_ tableView: UITableView, cellForRowAt indexPath: indexPath) -> UITableViewCell {} 这个函数。< / p >

而不是beginUpdates()/endUpdates(),建议调用现在是:

tableView.performBatchUpdates(nil, completion: nil)

关于beginUpdates/ endpdates,苹果说:“尽可能使用performBatchUpdates(_:completion:)方法而不是这个方法。”

看:https://developer.apple.com/documentation/uikit/uitableview/1614908-beginupdates

Swift 4及以上

将以下代码添加到tableview的didselect行委托方法中

tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()