的UITableViewCell上点击

问题描述:

扩大比方说,我们有一个自定义的UITableViewCell的UITableViewCell上点击

所以每当我单击单元格的自定义按钮..它应扩大到一定程度(你可以说40高度更多...),当我点击再次到相同的自定义按钮,它应该折叠到先前的高度。

开发者请指导我..我怎样才能实现这个任务

+0

注意:如果你想扩展区域包含新的可点击的细胞(行)(而不仅仅是扩大为单击的单元格的空间),参见[gcamp的答案](http://stackoverflow.com/a/ 199364分之7253288)。它开始于一组关闭的*部分*;点击部分标题打开该部分,显示其单元格。 – ToolmakerSteve 2017-03-12 01:30:00

执行heightForRowAtIndexPath来计算正确的高度。然后在您的按钮的代码,迫使表重新评估每个单元的高度与beginUpdates加endUpdates:

[self.tableView beginUpdates]; 
[self.tableView endUpdates]; 

更改到的tableview细胞的高度会自动heightForRowAtIndexPath计算和变化也将被动画。

实际上,您可能会选择单元格而不是您的单元格上的按钮,而不是在didSelectRowAtIndexPath中执行此操作。

我做了一个可重用的组件,将做你在谈论什么。这很容易使用,并有一个演示项目。

GCRetractableSectionController在GitHub上。

+0

这正是我所需要的。 UITableviewCell中的UITableview。完善。谢谢。 :) – ScarletWitch 2015-10-29 07:57:24

+0

对我来说,这是完美的解决方案 - 我想知道如何让扩大的区域包含更多可点击的单元行,而不仅仅是一个单元格。 – ToolmakerSteve 2017-03-12 01:25:09

我使用了Gcamp的源代码并创建了自己的版本。

1)在loadView方法中,初始化一个可变数组,您将在其中保存段的展开或非展开状态。将扩展状态保存在单独的数组中是非常重要的,当表视图滚动时不会破坏它们(例如,如果将它存储在headerView中,它将被重绘,并且忘记它展开或不展开的天气)。在我的情况下,它是_sectionStatuses数组。

- (void)loadView 
{ 
    // At the beginning all sections are expanded 
    _sectionStates = [NSMutableArray arrayWithCapacity:self.tableView.numberOfSections]; 
    for (int i = 0; i < self.tableView.numberOfSections; i++) { 
     _sectionStates[i] = [NSNumber numberWithBool:YES]; 
    } 
} 

2)为具有用于展开的按钮的部分创建自定义headerView。使用委托模式将您的headerView中按钮的动作委托给TableViewController。您可以在Gcamp的源代码中找到合适的图像。

3)创建一个操作来删除或添加行。这里_foldersArray是我的结构,它包含所有的数据。我的部分的headerView - MCExpandableAccountHeaderView知道它是自己的部分编号 - 当我为每个部分创建标题视图时,我将其转移到那里。将它转移到此方法非常重要,因为您必须知道哪个节现在被展开或拉伸。

- (void)expandClicked:(MCAccountHeaderView *)sender 
{ 
MCExpandableAccountHeaderView *expandableAccountHeaderView = (MCExpandableAccountHeaderView*)sender; 

// Finding a section, where a button was tapped 
NSInteger section = expandableAccountHeaderView.section; 

// Number of rows, that must be in a section when it is expanded 
NSUInteger contentCount = [_foldersArray[section - 1][@"folders"] count]; 

// Change a saved status of a section 
BOOL expanded = [_sectionStates[section] boolValue]; 
expanded = ! expanded; 
expandableAccountHeaderView.expanded = expanded; 
_sectionStates[section] = [NSNumber numberWithBool:expanded]; 

// Animation in a table 
[self.tableView beginUpdates]; 

NSMutableArray* modifiedIndexPaths = [[NSMutableArray alloc] init]; 
for (NSUInteger i = 0; i < contentCount; i++) { 
    NSIndexPath* indexPath = [NSIndexPath indexPathForRow:i inSection:section]; 
    [modifiedIndexPaths addObject:indexPath]; 
} 

if (expandableAccountHeaderView.expanded) [self.tableView insertRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 
else [self.tableView deleteRowsAtIndexPaths:modifiedIndexPaths withRowAnimation:UITableViewRowAnimationFade]; 

[self.tableView endUpdates]; 

// Scroll to the top of current expanded section 
if (expandableAccountHeaderView.expanded) [self.tableView scrollToRowAtIndexPath:INDEX_PATH(0, section) atScrollPosition:UITableViewScrollPositionTop animated:YES]; 
} 

4)根据扩展与否,在一个段中返回正确的数字或​​行也很重要。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    BOOL expanded = [_sectionStates[section] boolValue]; 

    return expanded ? [_foldersArray[section - 1][@"folders"] count] : 0; 
} 

因为这是完全正确的,所以我不打算在这里说任何与公认的答案相抵触的东西。但是,我将详细讨论如何实现这一点。如果您不想阅读所有这些内容,并且更愿意在工作项目中使用源代码,则我已上传example project to GitHub

其基本思想是在方法-tableView: heightForRowAtIndexPath:内部有一个条件,用于确定当前单元格是否应该展开。这将通过在-tableView: didSelectRowAtIndexPath:之内调用表格上的开始/结束更新来触发。在本例中,我将演示如何制作允许一次扩展一个单元格的表格视图。

您需要做的第一件事是声明对NSIndexPath对象的引用。不过你想要的,你可以这样做,但我建议使用这样的属性声明:

@property (strong, nonatomic) NSIndexPath *expandedIndexPath; 

注:你并不需要创建里面viewDidLoad中该指数的路径,或者其他类似方法。索引最初为零的事实只意味着表格最初不会有扩大的行。如果你宁愿表具有扩展您所选择的一排开始,您可以添加这样的事情您的viewDidLoad方法:

NSInteger row = 1; 
NSInteger section = 2; 
self.expandedIndexPath = [NSIndexPath indexPathForRow:row inSection:section]; 

的下一步是头顶到您的UITableViewDelegate方法-tableView: didSelectRowAtIndexPath:添加基于用户选择来改变扩展的小区索引的逻辑。这里的想法是检查刚才选择的索引路径是否存储在expandedIndexPath变量中的索引路径中。如果两者匹配,那么我们知道用户正试图取消选择扩展单元,在这种情况下,我们将该变量设置为零。否则,我们将expandedIndexPath变量设置为刚刚选择的索引。这一切都在beginUpdates/endUpdates调用之间完成,以允许表视图自动处理过渡动画。

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    [tableView beginUpdates]; // tell the table you're about to start making changes 

    // If the index path of the currently expanded cell is the same as the index that 
    // has just been tapped set the expanded index to nil so that there aren't any 
    // expanded cells, otherwise, set the expanded index to the index that has just 
    // been selected. 
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     self.expandedIndexPath = nil; 
    } else { 
     self.expandedIndexPath = indexPath; 
    } 

    [tableView endUpdates]; // tell the table you're done making your changes 
} 

然后,最后一步是在另一个UITableViewDelegate方法-tableView: heightForRowAtIndexPath:。此方法将在您为表确定需要更新的每个索引路径触发beginUpdates后调用。这是您将比较expandedIndexPath与当前正在重新评估的索引路径的位置。

如果两个索引路径相同,那么这是您希望展开的单元格,否则它的高度应该是正常的。我使用了100和44的值,但你可以使用任何适合你需要的东西。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    // Compares the index path for the current cell to the index path stored in the expanded 
    // index path variable. If the two match, return a height of 100 points, otherwise return 
    // a height of 44 points. 
    if ([indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     return 100.0; // Expanded height 
    } 
    return 44.0; // Normal height 
} 
+0

你能否解释一下table view在新版本中如何处理动画,其中`beginUpdates`和`endUpdates`之间什么都没有?谢谢 – Leo 2016-02-25 14:22:57

+0

应该是被接受的答案!很好的答案! – fabersky 2016-04-27 14:36:36

我为此创建了一个开源库。您只需在您的代码中实施折叠并展开代表,然后在代码中输入即可!!你也可以执行任何绘图和动画。退房this

enter image description here

+2

项目可以改进,如果你subview而不是使用viewWithTag的tableview单元格 – SleepsOnNewspapers 2015-06-03 01:21:23

initialize iSelectedIndex = -1; and declare 
UITableView *urTableView; 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ 

return 10; //Section count 

} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 

return 3; //row count 

} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

static NSString *CellIdentifier = @"Cell"; 

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

if(cell == nil) 
{ 
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 

} 

[cell.textLabel setText:[NSString stringWithFormat:@"sec:%d,row:%d",indexPath.section,indexPath.row]]; 

return cell; 

} 


- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{ 

// adding a label with the tap gesture to the header in each section 

headerLabel = [[UILabel alloc]init]; 

headerLabel.tag = section; 

headerLabel.userInteractionEnabled = YES; 

headerLabel.backgroundColor = [UIColor greenColor]; 

headerLabel.text = [NSString stringWithFormat:@"Header No.%d",section]; 

headerLabel.frame = CGRectMake(0, 0, tableView.tableHeaderView.frame.size.width, tableView.tableHeaderView.frame.size.height); 

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(gestureTapped:)]; 

[headerLabel addGestureRecognizer:tapGesture]; 

return headerLabel; 

} 

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{ 

return 50.0; //adjust the height as you need 

} 

- (void)gestureTapped:(UITapGestureRecognizer *)sender{ 

UIView *theSuperview = self.view; // whatever view contains 

CGPoint touchPointInSuperview = [sender locationInView:theSuperview]; 

UIView *touchedView = [theSuperview hitTest:touchPointInSuperview withEvent:nil]; 

if([touchedView isKindOfClass:[UILabel class]]) 
{ 

    if (iSelectedIndex != touchedView.tag) { //if new header is selected , need to expand 

     iSelectedIndex = touchedView.tag; 

    }else{ // if the header is already expanded , need to collapse 

     iSelectedIndex = -1; 

    } 

    [urTableView beginUpdates]; 

    [urTableView endUpdates]; 

} 

} 

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

// Show or hide cell 

float height = 0.0; 

if (indexPath.section == iSelectedIndex) { 

    height = 44.0; // Show the cell - adjust the height as you need 

} 

return height; 

} 

而不是使用[tableView beginUpdates][tableView endUpdates]的,我使用的是didSelectRowAtIndexPath方法里面[tableView reloadRowsAtIndexPath:... withRowAnimation:...]方法。

我更喜欢这个,因为当我展开我的UITableViewCell时,当我使用开始&末端更新方法时,我有一些应该显示的元素的问题。另一点是,你可以像一些动画之间进行选择:上,下,左,右......

为了增加为0x7FFFFFFF的答案,我发现我需要一个额外的条件if语句didSelectRowAtIndexPath方法内 - 因此:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

    [tableView beginUpdates]; 

    if (self.expandedIndexPath && [indexPath compare:self.expandedIndexPath] == NSOrderedSame) { 
     self.expandedIndexPath = nil; 
    } else { 
     self.expandedIndexPath = indexPath; 
    } 

    [tableView endUpdates]; 

} 

这是米克的答案,但对于斯威夫特4.(IndexPath取代NSIndexPath,它配备了一个空IndexPath为无会崩溃斯威夫特此外,比较方法是现在不同了。您不能再使用NSOrderedSame)

声明expandedIndexPath属性。

var expandedIndexPath = IndexPath() 

可选的viewDidLoad部分。

expandedIndexPath = IndexPath(row: 1, section: 2) 

然后didSelectRow部分。

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 
    tableView.beginUpdates() 

    if indexPath.compare(expandedIndexPath) == .orderedSame { 
     expandedIndexPath = IndexPath() 
    } else { 
     expandedIndexPath = indexPath 
    } 

    tableView.endUpdates() 
} 

然后是heightForRow部分。

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 
    if indexPath.compare(expandedIndexPath) == .orderedSame { 
     return 100 
    } 

    return 44 
}