I am new to iOS development. I have doubt related to UITableViewCell height.
I am using one tableview, I have taken prototype cell in storyboard. The cell height I have given is 75. But what I need is when I select cell height will be increase and show 1 button (which is in black color in the image). The button is declared in UITableViewCell's custom class. Below of that image I have one label. If button is showing I need to place the label at the bottom of button or else in the place of button I need to show that label.
Thanks in advance.......
You can use [tableView beginUpdates] and [tableView endUpdates];
for Ex:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self hideControlMethodsTable];//Declare method to hide when button tableview loads initially
[tableList beginUpdates];
[tableList deselectRowAtIndexPath:indexPath animated:YES];
CellRes = (ReservationTableViewCell*)
[tableList cellForRowAtIndexPath:indexPath];
if (cellSelectedFirstTime == true) {
self.selectedRow = indexPath.row;
[self showControlsMethodsTable];
cellSelectedFirstTime = false;
}
else{
if(self.selectedRow == indexPath.row){
self.selectedRow = -1;
[self hideControlMethodsTable];
}
else{
self.selectedRow = indexPath.row;
[self showControlsMethodsTable];
}
}
[tableList endUpdates];
}
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(openForFirstCell == true){
if (cellSelectedFirstTime==true) {
CellRes = [UIColor lightGrayColor];
return 180.;
}
return 60;
}
}
Hope this helps you.
Related
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];
}
I have a TableViewController that initialize my cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FIDPostTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell for this indexPath
[cell updateFonts];
[cell loadDataWithPost:[self.posts objectAtIndex:indexPath.item]];
cell.parentTableViewController = self;
cell.indexPath = indexPath;
[cell draw];
if(self.selectedIndex == indexPath.row){
//Do expand cell stuff
} else{
//DO closed cell stuff
}
return cell;
}
and that responds to heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *reuseIdentifier = CellIdentifier;
FIDPostTableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
if (!cell) {
cell = [[FIDPostTableViewCell alloc] init];
[self.offscreenCells setObject:cell forKey:reuseIdentifier];
}
// Configure the cell for this indexPath
[cell updateFonts];
[cell loadDataForHeightCalculationWithPost:[self.posts objectAtIndex:indexPath.item]];
[cell draw];
if(self.selectedIndex == indexPath.row){
return [cell calculateHeight] + 100;
} else{
return [cell calculateHeight];
}
}
self.selectedIndex is a int local variable of TableViewController
Each custom cell have inside a button, that respond to a selector when touched, this is my CustomViewCell code:
self.expandSocialAction = [UIButton buttonWithType:UIButtonTypeRoundedRect];
self.expandSocialAction.backgroundColor = [FIDUIHelper fideniaLightBlue];
[self.expandSocialAction addTarget:self action:#selector(selectRow:) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.expandSocialAction];
and then:
-(void)selectRow:(id)sender{
if(self.parentTableViewController.selectedIndex == self.indexPath.row){
self.parentTableViewController.selectedIndex = -1;
} else{
self.parentTableViewController.selectedIndex = self.indexPath.row;
}
[self.parentTableViewController.tableView beginUpdates];
[self.parentTableViewController.tableView endUpdates];
}
The cell have a pointer to parent tableViewContorller: self.parentTableViewController.
All work fine, after beginUpdate and endUpdates call, the method heightForRowAtIndexPath is called ( i put a break point in ) and also che cell have the right height.
If i click the button on the first or second row the cell animate and change height fine, but if i scroll down the table and for example i click on the 6th row, the height change but the tableView scroll automatically to the first or second element.
Any suggestion?
Regards,
I have a feeling that the heightForRowAtIndexPath: method is part of the problem. Instead of performing the calculation, try to just use two constants in that method: one for expanded, one for collapsed. I'm wondering if the method is taking too long to execute and then thinks the height of the rows are 0, then the calculation finally returns, but the UITableView scroll position is not updated.
Another suggestion might be that your estimatedHeight and heightFor methods are returning two vastly different values.
check if you have used ScrollTORowAtIndexPAth someWhere in your code
I want to change image on the button, when button click. That button is placed in table cell. Image is changing successfull. But when table scroll by default after every 5 cell button image also changed, while I didn't changed it. Means new cells in table, unwilling change. This cell is reusable, and created by storyboard.
How to use that button as switch button. How can I achieve this.
- (NSIndexPath *)indexPathWithSubview:(UIView *)subview {
while (![subview isKindOfClass:[UITableViewCell self]] && subview) {
subview = subview.superview;
}
return [self.table indexPathForCell:(UITableViewCell *)subview];
}
- (IBAction)btnCheckedCellPressed:(id)sender{
NSIndexPath *myIP = [self indexPathWithSubview:(UIButton *)sender];
MyWantsTableViewCell* cell = (MyWantsTableViewCell *)[self.table cellForRowAtIndexPath:myIP];
if (cell.btnEdit.currentImage == [UIImage imageNamed:#"checkbox-active"]) {
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-deactive"]
forState:UIControlStateNormal];
} else {
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-active"]
forState:UIControlStateNormal];
}
// [self.table beginUpdates];
// [self.table reloadRowsAtIndexPaths:#[myIP] withRowAnimation:UITableViewRowAnimationFade];
// [self.table endUpdates];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
MyWantsTableViewCell *cell=nil;
static NSString *AutoCompleteRowIdentifier = #"MyWantsTableViewCellEdit";
cell = [tableView dequeueReusableCellWithIdentifier:AutoCompleteRowIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[MyWantsTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AutoCompleteRowIdentifier];
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:cell.imgProduct];
}
cell.selectionStyle = UITableViewCellAccessoryNone;
cell.btnEdit.tag = indexPath.row;
return cell;
}
Use simple way as I have described below, and use an object to keep the current selection, since it will go if you scroll the UITableView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-active"]
forState:UIControlStateNormal];
[cell.btnEdit setImage:[UIImage imageNamed:#"checkbox-deactive"]
forState:UIControlStateSelected];
MyClass *obj=[arr objextAtIndex:indexPath.row];
cell.btnEdit.selected=obj.selected;
}
- (IBAction)btnCheckedCellPressed:(id)sender{
NSIndexPath *myIP = [self indexPathWithSubview:(UIButton *)sender];
MyWantsTableViewCell* cell = (MyWantsTableViewCell *)[self.table cellForRowAtIndexPath:myIP];
cell.btnEdit.selected=!cell.btnEdit.selected;
MyClass *obj=[arr objectAtIndex:myIP.row];
obj.selected=!obj.selected;
}
Sample
Find sample for help here
1.. Take a NSInteger variable like "selectedBtn" and initialize it with -1, in viewDidLoad().
2.. go to cellForRowAtIndexPath() set button.tag == indexpath.row;
3.. Now in btnCheckedCellPressed() set that selectedBtn variable with tag value of that sender button.
4.. Now again go to cellForRowAtIndexPath() and place a check
if(cell.btnEdit.tag == selectedBtn)
// set selected image on button
else
// set unselected Image
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];
}
I have the following setup in my app:
I have a UITableView which has animated height change, when a cell is selected. In the cell is I have a label, which text I want to be on one line with truncation, when the cell is not selected and then when it is selected (the cell height get's bigger) I want the text (UILabel) to be on multiple lines, based on the text.
This is my code at the moment:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(self.selectedIndex != nil && [self.selectedIndex isEqual:indexPath]) {
return kCellHeight * 3.9;
}
return kCellHeight;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
ProductCell *cell = (ProductCell *) [tableView cellForRowAtIndexPath:indexPath];
if (![indexPath isEqual:self.selectedIndex]) {
cell.addButton.hidden = NO;
cell.descLabel.numberOfLines = 0;
cell.descLabel.lineBreakMode = NSLineBreakByWordWrapping;
[cell.descLabel sizeToFit];
tableView.scrollEnabled = NO;
self.selectedIndex = indexPath;
}else {
self.selectedIndex = nil;
cell.descLabel.lineBreakMode = NSLineBreakByTruncatingTail;
tableView.scrollEnabled = YES;
cell.addButton.hidden = YES;
}
[tableView beginUpdates];
[tableView endUpdates];
}
The first part (when the cell is selected, expanded and the height gets bigger) the text fits as I want, but the second part (when the cell is contracted, and the cell height get back to the normal height) the text does not fit to the new height and is truncated as I want it to, it just stays multi lined. How can I fix this? So when the cell is selected the text gets multilined and when the cell is deselected the text gets back to its normal truncated state?
EDIT:
Override
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
and add below line in this function
cell.descLabel.numberOfLines = 1;