UITableViewCell extends by click

Let's say we have a custom UITableViewCell

Therefore, whenever I click on a custom button on a cell, it should expand to some extent (you can say that the height is 40 more ...), and when I click on the same custom button again, it should collapse to the previous height.

Developer, please help me .. how can I achieve this task

+49
objective-c iphone uitableview uibutton
Jan 08 '11 at 18:09
source share
9 answers

Add heightForRowAtIndexPath to calculate the correct height. Then, in the code for your button, make the table overestimate each cell height with beginUpdates plus endUpdates:

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

Changes in table cell heights will be automatically calculated using heightForRowAtIndexPath, and the changes will also be animated.

In fact, instead of the button on your cell that does this, you can simply make a cell selection in this didSelectRowAtIndexPath .

+63
Sep 05 2018-11-11T00:
source share

I am not going to say anything to contradict the accepted answer, given that this is perfectly correct. However, I will tell you more about how to do this. If you don’t want to read all this and are more interested in playing with the source code in a working draft, I uploaded the project to GitHub .

The main idea is to have a condition inside the -tableView: heightForRowAtIndexPath: method, which determines whether the current cell should be expanded. This will be caused by invoking start / end updates in the table from -tableView: didSelectRowAtIndexPath: In this example, I will show how to create a table view that allows you to expand one cell at a time.

The first thing you need to do is declare a reference to the NSIndexPath object. You can do it however you want, but I recommend using a property declaration as follows:

 @property (strong, nonatomic) NSIndexPath *expandedIndexPath; 

NOTE. You do not need to create this index path inside viewDidLoad or any other similar method. The fact that the index is initially zero will mean that initially the table will not have an extended row. If you prefer the table to start with an extended row, you could add something like this to the viewDidLoad method:

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

The next step is to switch to the UITableViewDelegate method -tableView: didSelectRowAtIndexPath: to add logic to change the extended index of the cell based on user selection. The idea here is to check the just selected pointer path against the index path stored inside the expandedIndexPath variable. If they match, then we know that the user is trying to cancel the selected cell, in this case we set the variable to nil. Otherwise, we set the expandedIndexPath variable to the index just selected. All of this is done between calls to beginUpdates / endUpdates to allow the table view to automatically handle transient animations.

 - (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 } 

Then the last step is in another UITableViewDelegate -tableView: heightForRowAtIndexPath: method. This method is called after you beginUpdates once for each index path that the table determines needs to be updated. Here you compare the value of expandedIndexPath with the index that is currently being reevaluated.

If the two index paths are the same, then this is the cell you want to expand, otherwise the height should be normal. I used the values ​​100 and 44, but you can use what ever suits your needs.

 - (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 } 
+91
Sep 04 '13 at 18:09
source share

I created an open source library for this. You just crash and expand delegates in your code and voilΓ ! You can also perform any drawings and animations. this .

enter image description here

+9
Nov 26 '13 at 9:55
source share

I made a reusable component that will do exactly what you are talking about. It is fairly easy to use, and there is a demo project.

GCRetractableSectionController on GitHub.

+8
Aug 31 '11 at 6:21
source share

Instead of using [tableView beginUpdates] and [tableView endUpdates] , I use the [tableView reloadRowsAtIndexPath:... withRowAnimation:...] method inside the didSelectRowAtIndexPath method.

I prefer this because I had problems with items that should be displayed when I extend my UITableViewCell , when I used the start and end methods of the update. Another thing is that you can choose between some animations, such as: "Top", "Bottom", "Left", "Right" ...

+7
Sep 19 '14 at 9:53 on
source share

I used the Gcamp source code and made my own version.

1) In the loadView method, initialize a mutable array in which you save the expanded or unexpanded states of your partitions. It is extremely important to store the extended statuses in a separate array, which is not destroyed during scrolling of the table (for example, if you store it in the headerView, it will be redrawn and forget about the weather, which was expanded or not). In my case, it is an array of _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) Create a custom title for the section with a button for extension. Delegate the action using the button in the headerView on the TableViewController using the delegation template. You can find suitable images in the Gcamp source code.

3) Create an action to delete or add rows. Here _foldersArray is my structure containing all the data. My headerView header - MCExpandableAccountHeaderView knows its own section number - I transfer it there when I create the headers for each section. It is imperative to pass it to this method, since you need to know which section is now expanded or stretched.

 - (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) It is also important to return the correct number or lines in a section depending on how it expands or not.

 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { BOOL expanded = [_sectionStates[section] boolValue]; return expanded ? [_foldersArray[section - 1][@"folders"] count] : 0; } 
+2
May 13 '13 at 17:07
source share
 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; } 
0
Feb 19 '14 at 8:15
source share

To add 0x7fffffff to the response, I found that I needed an additional condition in the if statement in didSelectRowAtIndexPath - this way:

 - (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]; } 
0
Aug 23 '15 at 14:46
source share

This is Mick's answer, but for Swift 4. (IndexPath replaces NSIndexPath, which comes with an empty IndexPath, because nil will cause Swift to crash. Also, the comparison method is now different. You can no longer use NSOrderedSame)

Declare the expandIndexPath property.

 var expandedIndexPath = IndexPath() 

Additional part of viewDidLoad.

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

Then the didSelectRow part.

 override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.beginUpdates() if indexPath.compare(expandedIndexPath) == .orderedSame { expandedIndexPath = IndexPath() } else { expandedIndexPath = indexPath } tableView.endUpdates() } 

Then part of heightForRow.

 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { if indexPath.compare(expandedIndexPath) == .orderedSame { return 100 } return 44 } 
0
Nov 29 '17 at 16:31
source share



All Articles