I have a custom UITableViewCell, and I it expands on selection. What I want to do is make the cells height go back to normal (44), if the cell that was selected was reselected. Here is my code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([indexPath isEqual:self.selectedCell] && ![[tableView indexPathForSelectedRow] isEqual:indexPath]) {
return 100;
}
return 44;
}
The code works fine, but it seems to be ignoring the 2nd term in the if statement. Apparently I'm doing it wrong. Is there a way to fix it?
I don't know why you compare cell with indexPath.
[indexPath isEqual:self.selectedCell]
If you just want expand selected cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[tableView indexPathForSelectedRow] isEqual:indexPath])
{
return 100;
}
return 44;
}
UITableViewDelegate has deselect method
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
You need reload cell in this delegate
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
You need to store that particular index path in array to indicate that it is expanded Lets say you had selected 5 row that store that row number in an array, and reload that row after selecting it
Here is small code snippet that can help you to achieve this
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([_arrExpandedRows containsObject:indexPath.row]){
[_arrExpandedRows removeObject:indexPath.row];
}else{
// [_arrExpandedRows removeAllObjects]; // IF you only want to make it work for single row
[_arrExpandedRows addObject:indexPath.row];
}
[tableView reloadData];
}
And in your heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if([_arrExpandedRows containsObject:indexPath.row]){
{
return 100;
}
return 44;
}
So what will it do if your array contains object of expanded row it will make reduce its row height and back to 44 and if not it will make it back to 100.
And above code will also work for multiple rows to expand and collapse at same time.
I think what you want is unselect cell when tap on a selected cell.
Store selectedIndexPath and compare in delegate
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if ([selectedIndexPath isEqual:indexPath])
{
[tableView deselectRowAtIndexPath:indexPath animated:1];
}
else
{
selectedIndexPath = indexPath;
}
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
You can do something like this:
#interface ViewController () <UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, strong) UITableView* tableView;
#property (nonatomic, strong) NSMutableArray* selectedIndexPaths;
#end
<...>
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:CELL_ID];
cell.textLabel.text = [NSString stringWithFormat:#"%i", (int)indexPath.row];
return cell;
}
#pragma mark UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 44.0;
if ([_selectedIndexPaths containsObject:indexPath])
{
height = 100;
}
return height;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{//store or remove selected indexPaths
if ([_selectedIndexPaths containsObject:indexPath])
{
[_selectedIndexPaths removeObject:indexPath];
}
else
{
[_selectedIndexPaths addObject:indexPath];
}
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
Related
In a UITableView Controller, I have just added 'swipe to delete' by implementing tableView: commitEditingStyle: forRowAtIndexPath. Additionally, the rows can be selected to expand showing more content.
The undesired result after swiping:
The two lower rows remain in view after swiping until about about 0.5 seconds after the undelete animation completes.
A screenshot of IB:
The cell's contents have grown into the lower cell without it showing that it has been selected. (Selection causes the cell to increase height and give it a grayish background color.) This is occurring on every row in 2 similarly operating view controllers.
I have tried (without success) to intercept the 'selection' in several UITableViewDelegate methods, and cannot find out how to stop this from occurring. I have also tried setting the IB dynamic prototype cells to height: 85.
Looking for ideas on how to prevent this expansion from occurring.
EDIT
- (void)viewDidLoad {
....
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = kCellHeight;
....
}
#pragma mark - TableView delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
NSInteger *rows = (NSInteger *)[sectionInfo numberOfObjects];
if (!self.rowsInSection)
self.rowsInSection = rows;
if (rows > 0)
return [sectionInfo numberOfObjects];
else {
[tableView setSeparatorColor:[UIColor clearColor]];
[tableView setBounces:NO];
return 1;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = self.rowsInSection > 0 ? #"numberIdentifier" : #"noNumbersIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
if (self.rowsInSection > 0)
[self configureCell:cell atIndexPath:indexPath];
else
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// [self.arrayOfIndexPaths addObject:indexPath];
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow];
if (selectedIndexPath) {
if (tableView.editing)
return 85.0;
else if (selectedIndexPath.row == indexPath.row)
return 185.0;
}
return 85.0;
}
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)path {
if (tableView.editing)
return nil;
// If real rows exist, return the path, making row selectable
if (self.rowsInSection > 0)
return path;
// Otherwise do not allow the row to be selected
return nil;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][0];
if ([sectionInfo numberOfObjects] > 0)
// Return the contentView to stop the header from sliding with delete
return [tableView dequeueReusableCellWithIdentifier:#"numberHeaderIdentifier"].contentView;
else
return [tableView dequeueReusableCellWithIdentifier:#"emptyHeaderIdentifier"];
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 75;
}
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Number *aNumber = [self.fetchedResultsController objectAtIndexPath:indexPath];
[cell configureSubviewsInCell:cell withNumber:aNumber];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView beginUpdates];
[tableView endUpdates];
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext];
[context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]];
}
}
You should set the hidden property of the labels that you don't want to show when the table view cell is not selected. For example:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Number *aNumber = [self.fetchedResultsController objectAtIndexPath:indexPath];
UILabel *label1 = (UILabel *)[cell.contentView viewWithTag:501];
label1.text = [aNumber valueForKey:#"number"];
if (!cell.selected)
{
label1.hidden = YES;
}
else
{
label1.hidden = NO;
}
.....
}
Then in didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView beginUpdates];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
UILabel *label1 = (UILabel *)[cell.contentView viewWithTag:501];
label1.text = [aNumber valueForKey:#"number"];
label1.hidden = NO;
[tableView endUpdates];
}
You should look into subclassing UITableViewCell so you don't have to use tags to access subviews.
The 'hiding' solution posted by beowulf is a valid option. In addition and because the swipe (to begin editing) was causing subviews in the cell to become 'unclipped', a method needed to be overrided, like so:
// this method is called as the swipe to delete is started
- (void)tableView:(UITableView *)tableView willBeginEditingRowAtIndexPath:(nonnull NSIndexPath *)indexPath
{
// only hide for unexpanded (unselected) cells
if ([self.selectedRowIndex compare:indexPath] != NSOrderedSame)
{
NumberTableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// a cell subclass method to hide/unhide subviews that fall into
// the next cell below
[cell subViewsInCellShouldBeHidden:YES];
}
}
I have a UITableView. I use this code to resize the selected row:
-(void) viewWillAppear:(BOOL)animated {
....
[self.tableView registerNib:[UINib nibWithNibName:#"ICSceneDetailViewTableViewCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:IC_CELL_SCENE_EDITOR_CELL];
...
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(int) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == nil)
return 0;
if (self.scenes != nil) {
return self.scenes.count;
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ICSceneDetailViewTableViewCell *cell = (ICSceneDetailViewTableViewCell*)[tableView dequeueReusableCellWithIdentifier:IC_CELL_SCENE_EDITOR_CELL forIndexPath:indexPath];
if (cell == nil) {
cell = [[ICSceneDetailViewTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:IC_CELL_SCENE_EDITOR_CELL];
}
ICScene *scene = [self.scenes objectAtIndex:[indexPath row]];
[cell.titleTextField setText:scene.title];
return cell;
}
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedRow = indexPath.row;
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath row] == self.selectedRow) {
return 250;
}
if (IS_IPAD) {
return 80;
}
else {
return 40;
}
}
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
BUT, after selecting any row (name it A, and A become resized):
if I drag another row (name it B) then randomly other rows disappears (except A).
if I scroll tableview and back to drag on same row (B) then, again randomly, other(s) cell(s) disappear(s), but not always the same(s).
if I comment heightForRowAtIndexPath then drag behavior is normal, but row can't be resized :(
I already searched many similar answers (adjusting selectedBackgroundView on cell, backgroundView cell, drawRect on cell, etc) but no one solution/workaround works for this simple (i think) code.
Thanks in advance :)
Yeh, answering my own question.
I found an alternative to resize selected UITableViewCell without calling to reloadRowsAtIndexPaths.
Replacing reloadRowsAtIndexPaths with:
[self.tableView beginUpdates];
[self.tableView endUpdates];
refreshes visible rows without calling cellForRowAtIndexPath. Imho the problem where dequeuing cells on every row being reloaded.
The resulting code is:
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedRow = indexPath.row;
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
So, automagically there isn't any disappearing row :)
How can I replace selected customTableCellA with customTableCellB
I already have a table view working with customTableCellAs
I want to replace customTableCellA with customTableCellB when it is selected.
customTableCellB is 50 pixels taller than customTableCellA
How can I implement this properly?
Try this,
Create an #property array NSMutableArray *selectedIndexPathArray and instantiate it in viewDidLoad, then
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat cellHeight = //customTableCellA height
if ([self.selectedIndexPathArray containsObject:indexPath]) {
cellHeight = //customTableCellB height
} else {
cellHeight = //customTableCellA height
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
if ([self.selectedIndexPathArray containsObject:indexPath]) {
//Load customTableCellB
} else {
//Load customTableCellA
}
//Other code
...
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.selectedIndexPathArray containsObject:indexPath]) {
//If you want to remove the selection in second touch, do
//[self.selectedIndexPathArray removeObject:indexPath];
} else {
[self.selectedIndexPathArray addObject:indexPath];
}
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNonne];
//Select appropriate UITableViewRowAnimation
[tableView endUpdates];
}
I'm attaching an image because this is kinda hard to explain, I've also mocked up what I actually want it to look like. I'm going to try to explain it to so that others might find the question.
Maybe you've run up against this before and know a fix?
Here is the image of what I have alongside a mockup of what I want:
On the left, what I have, the items in the table are not separated by separator lines but the empty lines cells are separated. Ideally, the empty cells would not be shown and the table would just end letting the background show and the items in the table would have separators between their cells.
This is the corresponding code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
NSMutableArray *withoutThing = [NSMutableArray arrayWithArray:self.containerOfLists];
[withoutThing removeObjectAtIndex:indexPath.row];
self.containerOfLists = withoutThing;
[[NSUserDefaults standardUserDefaults] setObject:self.containerOfLists forKey:#"list of lists"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self.tableView reloadData];
}
else if (editingStyle == UITableViewCellEditingStyleInsert) {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.containerOfLists count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ListCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
cell.textLabel.text = self.containerOfLists[indexPath.row];
return cell;
}
Ok.
In the Storyboard, select your UITableView, set the Separator to Single Line, or by code with:
[yourTableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
In your code, do somewhere:
[yourTableView setBackgroundColor:[UIColor lightGrayColor]];
Or the gray color you want.
Add this to your datasources methods (as the same level as tableView: cellForRowAtIndexPath):
-(UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
return [[UIView alloc] init];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5; //return correct number
}
in ViewDidLoad method call:
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
Init cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ListCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (!cell)
{
//init the cell!
}
cell.textLabel.text = self.containerOfLists[indexPath.row];
return cell;
}
A very simple question...
can we change the height of only current row(the row which is clicked) in UItableView?
Note: I dont want any affect on size of remaining rows.
Yes you can. You need a variable that keeps the last selected row. For ex.:
#property (nonatomic, assign) NSInteger selectedRow;
...
Then implement the method
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row == self.selectedRow) {
return 100.;
}
return 44.;
}
and then, update the method:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if(self.selectedRow == indexPath.row)
self.selectedRow = -1;
else
self.selectedRow = indexPath.row;
//The magic that will call height for row and animate the change in the height
[tableView beginUpdates];
[tableView endUpdates];
}
NOTE: Initialise your self.selectedRow with -1 value in the beginning as the default is 0.
You can use heightForRowAtIndexPath for setting height for a row
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
You can place a check for NSIndexPath and return height for that particular indexpath
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.row == rowtochangeheight) {
return 60;
}
else
{
return 44;
}
}
Yes you can do, But you need to save indexpath of selected row and you must intialize selectedRow variable by negative value.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
selectedRow = indexpath.row;
[tblView reloadData];
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == selectedRow)
return 150.0f;
return 30;
}