Can you animate a height change on a UITableViewCell when selected? - ios

I'm using a UITableView in my iPhone app, and I have a list of people that belong to a group. I would like it so that when the user clicks on a particular person (thus selecting the cell), the cell grows in height to display several UI controls for editing the properties of that person.
Is this possible?

I found a REALLY SIMPLE solution to this as a side-effect to a UITableView I was working on.....
Store the cell height in a variable that reports the original height normally via the tableView: heightForRowAtIndexPath:, then when you want to animate a height change, simply change the value of the variable and call this...
[tableView beginUpdates];
[tableView endUpdates];
You will find it doesn't do a full reload but is enough for the UITableView to know it has to redraw the cells, grabbing the new height value for the cell.... and guess what? It ANIMATES the change for you. Sweet.
I have a more detailed explanation and full code samples on my blog... Animate UITableView Cell Height Change

I like the answer by Simon Lee. I didn't actually try that method but it looks like it would change the size of all the cells in the list. I was hoping for a change of just the cell that is tapped. I kinda did it like Simon but with just a little difference. This will change the look of a cell when it is selected. And it does animate. Just another way to do it.
Create an int to hold a value for the current selected cell index:
int currentSelection;
Then:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int row = [indexPath row];
selectedNumber = row;
[tableView beginUpdates];
[tableView endUpdates];
}
Then:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([indexPath row] == currentSelection) {
return 80;
}
else return 40;
}
I am sure you can make similar changes in tableView:cellForRowAtIndexPath: to change the type of cell or even load a xib file for the cell.
Like this, the currentSelection will start at 0. You would need to make adjustments if you didn't want the first cell of the list (at index 0) to look selected by default.

Add a property to keep track of the selected cell
#property (nonatomic) int currentSelection;
Set it to a sentinel value in (for example) viewDidLoad, to make sure that the UITableView starts in the 'normal' position
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
//sentinel
self.currentSelection = -1;
}
In heightForRowAtIndexPath you can set the height you want for the selected cell
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
int rowHeight;
if ([indexPath row] == self.currentSelection) {
rowHeight = self.newCellHeight;
} else rowHeight = 57.0f;
return rowHeight;
}
In didSelectRowAtIndexPath you save the current selection and save a dynamic height, if required
- (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];
}
}
In didDeselectRowAtIndexPath set the selection index back to the sentinel value and animate the cell back to normal form
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
// do things with your cell here
// sentinel
self.currentSelection = -1;
// animate
[tableView beginUpdates];
[tableView endUpdates];
}
}

Instead of beginUpdates()/endUpdates(), the recommended call is now:
tableView.performBatchUpdates(nil, completion: nil)
Apple says, regarding beginUpdates/endUpdates: "Use the performBatchUpdates(_:completion:) method instead of this one whenever possible."
See: https://developer.apple.com/documentation/uikit/uitableview/1614908-beginupdates

reloadData is no good because there's no animation...
This is what I'm currently trying:
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];
It almost works right. Almost. I'm increasing the height of the cell, and sometimes there's a little "hiccup" in the table view as the cell is replaced, as if some scrolling position in the table view is being preserved, the new cell (which is the first cell in the table) ends up with its offset too high, and the scrollview bounces to reposition it.

I don't know what all this stuff about calling beginUpdates/endUpdates in succession is, you can just use -[UITableView reloadRowsAtIndexPaths:withAnimation:]. Here is an example project.

I resolved with reloadRowsAtIndexPaths.
I save in didSelectRowAtIndexPath the indexPath of cell selected and call reloadRowsAtIndexPaths at the end (you can send NSMutableArray for list of element's you want reload).
In heightForRowAtIndexPath you can check if indexPath is in the list or not of expandIndexPath cell's and send height.
You can check this basic example:
https://github.com/ferminhg/iOS-Examples/tree/master/iOS-UITableView-Cell-Height-Change/celdascambiadetam
It's a simple solution.
i add a sort of code if help you
- (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];
}

Swift 4 and Above
add below code into you tableview's didselect row delegate method
tableView.beginUpdates()
tableView.setNeedsLayout()
tableView.endUpdates()

Try this is for expanding indexwise row:
#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;
}

just a note for someone like me searching for add "More Details" on custom cell.
[tableView beginUpdates];
[tableView endUpdates];
Did a excellent work, but don't forget to "crop" cell view.
From Interface Builder select your Cell -> Content View -> from Property Inspector select "Clip subview"

Heres a shorter version of Simons answer for Swift 3. Also allows for toggling of the cell's selection
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 Version of Simon Lee's answer .
// 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
}
Then in 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()
}
}
}
}

Yes It's Possible.
UITableView has a delegate method 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) {
}];
}
But in your case if the user scrolls and selects a different cell then u need to have the last selected cell to shrink and expand the currently selected cell reloadRowsAtIndexPaths: calls heightForRowAtIndexPath: so handle accordingly.

Here is my code of custom UITableView subclass, which expand UITextView at the table cell, without reloading (and lost keyboard focus):
- (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;
}

I used #Joy's awesome answer, and it worked perfectly with ios 8.4 and XCode 7.1.1.
In case you are looking to make your cell toggle-able, I changed the -tableViewDidSelect to the following:
-(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];
}
I hope any of this helped you.

Check this method after iOS 7 and later.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
Improvements have been made to this in iOS 8. We can set it as property of the table view itself.

Swift version of Simon Lee's answer:
tableView.beginUpdates()
tableView.endUpdates()
Keep in mind that you should modify the height properties BEFORE endUpdates().

Inputs -
tableView.beginUpdates()
tableView.endUpdates()
these functions will not call
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
But, if you do,
tableView.reloadRows(at: [selectedIndexPath! as IndexPath], with: .none)
It will call the
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {}
this function.

I just resolved this problem with a little hack:
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;
}
When I need to expand the cell height I set isInEdit = YES and call the method [self onTimer] and it animates the cell growth until it reach the s_CellHeightEditing value :-)

Get the indexpath of the row selected. Reload the table. In the heightForRowAtIndexPath method of UITableViewDelegate, set the height of the row selected to a different height and for the others return the normal row height

Related

UITableviewCell height not getting reset on scroll

I have a tableview which can be expanded on selecting the cell and collapses on selecting again. When you select, the cell should expand and display a label and when you select again it collapses and hides the label . The expanding and collapsing works fine, but if i scroll the tableview after expanding a cell it behaves weird. Once it goes out of the view and comes back , the cell will have the expanded cell's height but the label which is supposed to be shown in expanded cell is hidden.If i select the cell again it collapses and displays the label. I use ,
- (CGFloat)tableView:(UITableView *)t heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:t cellForRowAtIndexPath:indexPath];
if([self cellIsSelected:indexPath])
return cell.frame.size.height+35;
return cell.frame.size.height;
}
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
NSNumber *selectedIndex = [self.selectedIndexes objectForKey:indexPath];
return selectedIndex == nil ? FALSE : [selectedIndex boolValue];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Deselect cell
NSLog(#"Select cell:%#",indexPath);
[self.tableView deselectRowAtIndexPath:indexPath animated:TRUE];
if([self pickTaskForIndexPath:indexPath].productSpecialMessage){
BOOL isSelected = ![self cellIsSelected:indexPath];
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
if(cell.messageLabel.hidden==true){
cell.messageLabel.hidden = false;
} else {
cell.messageLabel.hidden = true;
}
NSLog(#"message:%#",cell.messageLabel.text);
[cell layoutIfNeeded];
}
self.tableView.rowHeight=UITableViewAutomaticDimension;
[self.tableView beginUpdates];
[self.tableView endUpdates];
}
indexPath is added to the selectedIndexes on didSelectRowAtIndexPath
Please help me
Cells should be configured only within cellForRowAtIndexPath. When a state change occurs that makes a cell need to look different, just reload that cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PickTaskTableviewCell *cell = (PickTaskTableviewCell *)[tableView dequeueReusableCellWithIdentifier:#"cell"];
// everything else you do to configure the cell goes here, then ...
// check the logic here, we want one condition that tells us whether to show the labels
if([[self cellIsSelected:indexPath] && self pickTaskForIndexPath:indexPath].productSpecialMessage){
// don't need these here
//NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
// [self.selectedIndexes setObject:selectedIndex forKey:indexPath];
// PickTaskTableviewCell *cell= [self.tableView cellForRowAtIndexPath:indexPath];
cell.message.hidden=false;
cell.messageLabel.text=[self pickTaskForIndexPath:indexPath].productSpecialMessage;
cell.messageLabel.lineBreakMode=NSLineBreakByTruncatingTail;
cell.messageLabel.numberOfLines=3;
cell.messageLabel.hidden=NO;
} else {
cell.message.hidden=YES;
cell.messageLabel.hidden=YES;
}
NSLog(#"message:%#",cell.messageLabel.text);
// don't need this here
// [cell layoutIfNeeded];
return cell;
}
Selection (and presumably deselection) cause the need to update the cell, so...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// don't deselect it here, just reload it
// more on this later...
[self.selectedIndexes setObject:selectedIndex forKey:indexPath];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
// probably do the same in didDeselectRowAtIndexPath:
One last (optional) point. There's no need to maintain your own list of selected index paths, UITableView does that for you, so you could delete your selectedIndexes property and just use the table view methods, e.g....
- (BOOL)cellIsSelected:(NSIndexPath *)indexPath {
// Return whether the cell at the specified index path is selected or not
return [[self.tableView indexPathsForSelectedRows] containsObject:indexPath];
}

get cell from indexPath for expand the row when clicked?

I know there is a method called [tableView cellForRowAtIndexPath:indexPath]; which we can use to get cell at given indexPath.But i want to expand row when the row tapped.using this code-
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Deselect cell
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
// Toggle 'selected' state
BOOL isSelected = ![self cellIsSelected:indexPath];
// Store cell 'selected' state keyed on indexPath
NSNumber *selectedIndex = [NSNumber numberWithBool:isSelected];
int sec = indexPath.section;
int row = indexPath.row;
NSString* indexpathStr = [NSString stringWithFormat:#"%d-%d", sec, row];
[selectedIndexes setObject:selectedIndex forKey:indexpathStr];
// This is where magic happens...
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 0.0;
if([self cellIsSelected:indexPath]) {
//problem here
CalEventCell* cell = (CalEventCell*)[tableView cellForRowAtIndexPath:indexPath];
[self setDescriptionFrame:cell.descriptionLbl];
height = cell.descriptionLbl.frame.size.height+cell.eventLbl.frame.size.height +20;
}
else
height = 60;
return height;
}
when a row tapped i call beginUpdates and endUpdates methods on tableview and change row height in heightForAtIndexPath method.The problem is that as you can see i call cellForAtIndexPath from heightForAtIndexPath and in cellForRow there is beginUpdates method call which turn into a call to heightForRow and this loop never ends and program crash with EXC-BAD-EXCEP.
So any other method to get cell from indexPath or any other way to do so? Any help would be appreciated.
Take an array to store the selected indices and then reload that tableview.
If you tap that cell height will expand and again if you will tap it will decrease its height.
Check out the below code.
// Add a mutable array to your class
NSMutableArray *selectedCellArray;
// Initialise it in `viewDidLoad`
-(void)viewDidLoad
{
[super viewDidLoad];
selectedCellArray = [[NSMutableArray alloc] init];
}
// Add selected indices in selectedCellArray
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Deselect cell
[tableView deselectRowAtIndexPath:indexPath animated:TRUE];
[tableView reloadData];
if (![selectedCellArray containsObject:indexPath])
[selectedCellArray addObject:indexPath];
else
[selectedCellArray removeObject:indexPath];
[tableView beginUpdates]; // Animate the height change
[tableView endUpdates];
}
// Write the UI Updation in HeightForRowAtIndexPath Method
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
CGFloat height = 0.0;
if (selectedCellArray.count>0) {
if ([selectedCellArray containsObject:indexPath]) {
CalEventCell* cell = (CalEventCell*)[tableView cellForRowAtIndexPath:indexPath];
[self setDescriptionFrame:cell.descriptionLbl];
height = cell.descriptionLbl.frame.size.height+cell.eventLbl.frame.size.height+20;
return height;
}else{
height = 60.f;
return height;
}
}
return height;
}
Hope This Helps
You should call below lines of code on your didSelectRowAtIndexPath method
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:row inSection:sec]] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
When row will be reloaded cellForRowAtIndexPath:indexPath and heightForRowAtIndexPath methods will be called where u can expand cell's height
You can't use cellForRowAtIndexPath in heightForRowAtIndexPath if you are using reusable cells. You need to calculate the height in some other way. The simplest way that would likely work for you would be to keep a prototype cell in your class that you only use for sizing purposes.
Try this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[your_tableView reloadData];
}

How can I have a UITableViewCell pre-selected when it's view loads?

I'm working on something that plays an sound when a cell is selected in a table view. The tableView is populated by an Array of NSStrings with sound names.
I have an object as part of the controller that has a sound object, and when you click the cell it plays the sound objects correct file for the name.
How can I have a TableView cell pre selected when the view first loads, and have it so it doesn't play the sound when the tableView first loads.
Here's my selection code as of now.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TMVSoundCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SoundCell" forIndexPath:indexPath];
[self configureCell:cell forRowAtIndexPath:indexPath];
return cell;
}
- (void)configureCell:(TMVSoundCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.soundTitle.text = [[self.soundArray objectAtIndex:indexPath.row]name];
if (self.selectedRowIndexPath != nil && [indexPath compare:self.selectedRowIndexPath] == NSOrderedSame)
{
cell.soundTitle.textColor = [UIColor whiteColor];
self.itemView.item.sound = [self.soundArray objectAtIndex:indexPath.row];
[DataManager saveContext];
}
else
{
cell.soundTitle.textColor = AppDelegate.atmosphere.currentColor;
}
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *indexPaths = [NSMutableArray arrayWithObject:indexPath];
if (self.selectedRowIndexPath)
{
if ([indexPath compare:self.selectedRowIndexPath] == NSOrderedSame)
{
self.selectedRowIndexPath = nil;
}
else
{
[indexPaths addObject:self.selectedRowIndexPath];
self.selectedRowIndexPath= indexPath;
}
}
else
{
self.selectedRowIndexPath = indexPath;
}
[tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationAutomatic];
}
Use selectRowAtIndexPath to select a cell programmatically.
According to the docs:
Calling this method does not cause the delegate to receive a
tableView:willSelectRowAtIndexPath: or
tableView:didSelectRowAtIndexPath: message, nor will it send
UITableViewSelectionDidChangeNotification notifications to observers.
You need to call selectRowAtIndexPath in the cellForRowAtIndexPath. An example in Swift4 :
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if ADD_YOUR_CONDITION_HERE {
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .top)
}
return cell
}
In viewDidAppear add this code (in viewDidAppear to make sure your tableView has loaded)
UITableViewCell *cellToSelect = [self.tableView cellForRowAtIndexPath:self.selectedRowIndexPath];
[cellToSelect setHighlighted:YES animated:YES];
Your cell will be selected without calling the delegate method, so the sound won't be played.
You can call the
- (void)selectRowAtIndexPath:(NSIndexPath *)indexPath animated:(BOOL)animated scrollPosition:(UITableViewScrollPosition)scrollPosition
method in your viewDidLoad or viewWillAppear and it will select the row and not call your didSelectRowDelegate
Hope that helps
Cong
[self.soundArray enumerateObjectsUsingBlock:^(Sound *obj, NSUInteger idx, BOOL *stop)
{
if ([obj.name isEqualToString:self.itemView.item.sound.name])
{
[self tableView:self.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:idx inSection:0]];
}
}];
I'm such an idiot. I never thought of putting this in the my conifgureView method.
Thank you to everyone who helped me out.
In your cellForRowAtIndexPath method, check to see if the cell is for the row you want to be selected. If it is, set it selected. cell.selected = YES
Alternatively, [self.tableView selectRowAtIndexPath:self.selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
[self.tableView didSelectRowAtIndexPath:self.selectedIndexPath];`

How to resize cell height and the row height in ios

I have a UITableView that display a program name and the DJ name. For some programmes, there are no any DJs. So I created a custom UITableViewCell by putting 2 UILabels. Middle of the UITableViewCell displays the programme name and bottom UILabel displays the DJ name. To display both programme and the DJ name I have to set the cell size and UITableViewCell size as 147. But when DJ is not available I want to hide that bottom label and resize the cell and row height to 70. so how should I do this ?
I am checking if this DJ is null or not inside my cellForRowAtIndexPath method.
How I can resize the cell, and row height ?
Thanks for the help.
You need to use the following delegate:
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*) indexPath {
if(...){
return 147.0f; //or whatever
}
else{
return 70.f;
}
}
Add NSIndexPath below #implementation
NSIndexPath *selectedCellIndexPath;
Add Both of these methods.
// And in the implementation file:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
selectedCellIndexPath = indexPath;
// Forces the table view to call heightForRowAtIndexPath
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
// Note: Some operations like calling [tableView cellForRowAtIndexPath:indexPath]
// will call heightForRow and thus create a stack overflow
if(selectedCellIndexPath != nil
&& [selectedCellIndexPath compare:indexPath] == NSOrderedSame)
return 338;
return 64;
}
You have 2 types of cell. With height of 147 and 70 pixels.
1) As Anoop Vaidya said you should set cell height by checking "DJ is null or not".
2) To avoid reusing wrong cell template, you should use 2 types of cellIdentifier:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = nil;
if (check DJ is nil or not) {
CellIdentifier = #"CellWithHeight_147";
} else {
CellIdentifier = #"CellWithHeight_70";
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
...
return cell;
}

How to remove cell from static UITableView created in Storyboard

This should be easy, but I'm having trouble.
I have a static UITableView with a cell that I would like to remove programmatically if it's not needed.
I have a IBOutlet for it
IBOutlet UITableViewCell * cell15;
And I can remove it by calling
cell15.hidden = true;
This hides it, but leaves a blank space where the cell used to be and I can't get rid of it.
Perhaps a hack would be to change the height of it to 0?
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:indexPath
{
//what would I put here?
}
Thanks so much!
You can't really deal with this in the datasource since with static tables you don't even implement the datasource methods. The height is the way to go.
Try this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell == cell15 && cell15ShouldBeHidden) //BOOL saying cell should be hidden
return 0.0;
else
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
Update
It appears that, under autolayout, this may not be the best solution. There is an alternative answer here which may help.
You can use tableView:willDisplayCell and tableView:heightForRowAtIndexPath with the cell identifier to show/hide static tableview cells, but yo must implement heightForRowAtIndexPath referring to super, not to self. These two methods work fine for me:
(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([cell.reuseIdentifier.description isEqualToString:#"cellCelda1"]) {
[cell setHidden:YES];
}
}
and
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if ([cell.reuseIdentifier.description isEqualToString:#"cellCelda1"]) {
return 0;
}
return cell.frame.size.height;
}
Depending on how your table is supposed to work, in your data source you can implement tableView:numberOfRowsInSection: to return 0 rows for the section based on your necessary logic.
Updated for your comment:
The section parameter will be populated by iOS when your implementation is called so all you need is a switch to handle the section that has the row you ant removed/hidden. Example below:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
switch(section) {
case 0: // first section of your table, change for your situation
return 0;
default:
return 0;
}
}
It for only constant cell
-(void)tableViewSearchPeopleCellHide:(BOOL)hide{
searchCellShouldBeHidden=hide;
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0]];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES];
cell.hidden=hide;
self.searchPeople.hidden=hide;//UILabel
[self.tableView reloadData];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (searchCellShouldBeHidden) //BOOL saying cell should be hidden
return 0.0;
else
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
The first thing you can do is tag the cell from storyboard which you want to hide.
Put some standard number which you can identify.
Add this code.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
if (cell.tag==10) { //I have put 10 for some static cell.
cell.hidden=YES;
return 0;
}
cell.hidden = NO;
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
Set the cell you want to hide to hidden somewhere in your code. Add this code: (If your cell has different row height, then you need to override more functions)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int rowCount=0;
for ( int row=0; row<[super tableView:tableView numberOfRowsInSection:section]; ++row){
NSIndexPath* path=[NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell* cell=[super tableView:tableView cellForRowAtIndexPath:path];
if (!cell.hidden){
++rowCount;
}
}
return rowCount;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
int realRow=-1;
for ( int row=0; row<[super tableView:tableView numberOfRowsInSection:indexPath.section]; ++row){
NSIndexPath* path=[NSIndexPath indexPathForRow:row inSection:indexPath.section];
UITableViewCell* cell=[super tableView:tableView cellForRowAtIndexPath:path];
if (!cell.hidden){
++realRow;
}
if (realRow==indexPath.row)
return cell;
}
return nil;
}
Use index path to identify the cell in tableview height delegate and return 0
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if someCondition {
if indexPath.row == 1 || indexPath.row == 2 || indexPath.row == 3 {
return 0
}
}else{
if indexPath.row == 4 {
return 0
}
}
return super.tableView(tableView, heightForRowAt: indexPath)
}

Resources