Answers:
我发现了一个非常简单的解决方案,这是UITableView
我正在研究的副作用.....
将像元高度存储在通常通过来报告原始高度的变量中tableView: heightForRowAtIndexPath:
,然后在要对高度变化进行动画处理时,只需更改变量的值并调用它即可。
[tableView beginUpdates];
[tableView endUpdates];
您会发现它并不会完全重载,但是足以UITableView
让它知道必须重绘单元格,获取单元格的新高度值....然后猜猜是什么?为您动画化变化。甜。
我的博客上有更详细的解释和完整的代码示例。Animate UITableView单元格高度变化
You can also use this method followed by the endUpdates method to animate the change in the row heights without reloading the cell.
developer.apple.com/library/ios/documentation/UIKit/Reference/...
我喜欢西蒙·李的回答。我实际上并没有尝试该方法,但看起来它将更改列表中所有单元格的大小。我希望只更改被窃听的单元格。我有点像西蒙(Simon),但有一点点不同。选定单元格后,它将改变其外观。它确实具有动画效果。只是另一种方式。
创建一个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];
}
}
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];
它几乎可以正常工作。几乎。我在增加单元格的高度,有时在替换表格单元格时在表格视图中会有一点“打cup”,好像保留了表格视图中的某些滚动位置一样,新的单元格(这是第一个单元格在表格中)的偏移量过高而结束,滚动视图会弹起以重新定位。
我解决了 reloadRowsAtIndexPaths
。
我保存在didSelectRowAtIndexPath
所选单元格的indexPath中并调用reloadRowsAtIndexPaths
在最后(您可以将NSMutableArray发送给要重新加载的元素列表)。
在 heightForRowAtIndexPath
您可以检查indexPath是否在expandIndexPath单元格的列表中,并发送高度。
您可以检查以下基本示例: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];
}
现在,建议的调用不是beginUpdates()
/ endUpdates()
,而是:
tableView.performBatchUpdates(nil, completion: nil)
苹果公司说,关于beginUpdates / endUpdates:“尽可能使用performBatchUpdates(_:completion :)方法而不是此方法。”
参见:https : //developer.apple.com/documentation/uikit/uitableview/1614908-beginupdates
尝试这样做是为了扩展按索引的行:
@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;
}
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
}
迅捷版的Simon Lee的答案。
// 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:
以便进行相应处理。
这是我的自定义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];
}
希望这些对您有所帮助。
Swift版本的Simon Lee的答案:
tableView.beginUpdates()
tableView.endUpdates()
请记住,您应该在BEFORE之前 修改高度属性endUpdates()
。
输入-
tableView.beginUpdates()tableView.endUpdates() 这些函数将不会调用
func tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell {}
但是,如果这样做, tableView.reloadRows(at:[selectedIndexPath!as IndexPath],带有:.none)
它将调用 func tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath)-> UITableViewCell {} 此函数。
我只是通过一点技巧解决了这个问题:
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_CellHeightEditing值:-)
获取所选行的索引路径。重新加载表格。在UITableViewDelegate的heightForRowAtIndexPath方法中,将所选行的高度设置为不同的高度,对于其他行,返回正常的行高
[table reloadData]
结果会立即发生高度变化,而不是进行动画处理。