After browsing the internet for various different ways of making expanding/collapsible UITableViewCells, I started my approach earlier today.
I have an array that holds the state of each row in my UITableView(COLLAPSED=0, EXPANDED=1). When the expand button is clicked, I reverse the state in the array, and call reloadSection on whichever section needs to be updates.
In my delegate method heightForRowAtIndexPath, I check to see whether the cell should be expanded or not and return the proper height. The problem I am facing is that my cell does indeed expand, but instead of showing my content hid underneath(I made the cell much bigger with extra info underneath shortened it in IB, and enabled clip subviews to fix the cell overlap), it shows two other grouped UITableViewCells with a similar background as mine and throws the positioning of some labels/buttons off.
I am including pictures of what it looks like normally and expanded. I have gone through the code and it all looks okay, I think there may be a setting I need to change in order for this to render properly.
Here is the source:
#define UITABLEVIEWCELLSIZE_COLLAPSED 97
#define UITABLEVIEWCELLSIZE_EXPANDED 285
#define EXPANDED 1
#define COLLAPSED 0
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *state = [cellExpansionArray objectAtIndex:[indexPath section]];
if ([state intValue] == COLLAPSED) {
return UITABLEVIEWCELLSIZE_COLLAPSED;
}else if([state intValue] == EXPANDED) {
return UITABLEVIEWCELLSIZE_EXPANDED;
}
return UITABLEVIEWCELLSIZE_COLLAPSED;
}
//expandCell is called when the arrow indicator is clicked on the cell
-(IBAction)expandCell:(id)sender
{
NSInteger cellNumber = [sender tag];
NSNumber *state = [cellExpansionArray objectAtIndex:cellNumber];
if (state.intValue == 0) {
[cellExpansionArray replaceObjectAtIndex:cellNumber withObject:[NSNumber numberWithInt:EXPANDED]];
}else if (state.intValue == 1) {
[cellExpansionArray replaceObjectAtIndex:cellNumber withObject:[NSNumber numberWithInt:COLLAPSED]];
}
//[self.tableView beginUpdates];
//[self.tableView endUpdates];
//[self.tableView reloadSections:[[NSIndexSet alloc] initWithIndex:cellNumber] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"PCFCustomCell";
PCFCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//cell.autoresizingMask = UIViewAutoresizingFlexibleHeight;
[cell setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:#"1slot.png"]]];
PCFClassModel *course = [classesOffered objectAtIndex:[indexPath section]];
cell.courseCRN.text = [course CRN];
cell.courseDataRange.text = [course dateRange];
cell.courseDaysOffered.text = [course days];
cell.courseHours.text = [course credits];
cell.courseInstructor.text = [course instructor];
cell.courseTime.text = [course time];
cell.courseLocation.text = [course classLocation];
cell.courseSection.text = [course sectionNum];
cell.courseName.text = [course courseNumber];
cell.courseTitle.text = [course classTitle];
cell.courseType.text = [course scheduleType];
//cell.professorEmail.text = [course instructorEmail];
[cell.mailProf setTag:[indexPath section]];
[cell.showCatalog setTag:[indexPath section]];
[cell.followClass setTag:[indexPath section]];
[cell.addToSchedule setTag:[indexPath section]];
[cell.buttonDropDown setTag:[indexPath section]];
[cell.mailProf setHidden:NO];
[cell.mailProf setEnabled:YES];
if (![course instructorEmail]) {
[cell.mailProf setTitle:#"NO EMAIL PROVIDED" forState:UIControlStateNormal];
[cell.mailProf setEnabled:NO];
}
[cell.followClass setBackgroundImage:[UIImage imageNamed:#"search_res_11.png"] forState:UIControlStateNormal];
[cell.addToSchedule setBackgroundImage:[UIImage imageNamed:#"search_res_09.png"] forState:UIControlStateNormal];
if ([savedResults count] > 0) {
for (PCFClassModel *courseTwo in savedResults) {
if ([[courseTwo CRN] isEqualToString:[course CRN]]) {
[[cell followClass] setBackgroundImage:[UIImage imageNamed:#"favorite_activated.png"] forState:UIControlStateNormal];
break;
}
}
}
//schedule
if ([savedSchedule count] > 0) {
for (PCFClassModel *courseTwo in savedSchedule) {
if ([[courseTwo CRN] isEqualToString:[course CRN]]) {
[[cell addToSchedule] setBackgroundImage:[UIImage imageNamed:#"purdue_activated.png"] forState:UIControlStateNormal];
break;
}
}
}
[cell.mailProf addTarget:self action:#selector(mailProf:) forControlEvents:UIControlEventTouchUpInside];
[cell.showCatalog addTarget:self action:#selector(showCatalog:) forControlEvents:UIControlEventTouchUpInside];
[cell.followClass addTarget:self action:#selector(followClass:) forControlEvents:UIControlEventTouchUpInside];
[cell.addToSchedule addTarget:self action:#selector(addToSchedule:) forControlEvents:UIControlEventTouchUpInside];
[cell.buttonDropDown addTarget:self action:#selector(expandCell:) forControlEvents:UIControlEventTouchUpInside];
//get internet data
/*
[[cell staticAvailable] setHidden:YES];
[[cell available] setHidden:YES];
[cell.available setText:#""];
[[cell remainingActivityIndicator] startAnimating];
dispatch_queue_t getSpots = dispatch_queue_create("GetSpots", nil);
dispatch_async(getSpots, ^{
NSString *webData = nil;
while (!webData && self.view.window) webData = [PCFWebModel queryServer:[course classLink] connectionType:nil referer:#"https://selfservice.mypurdue.purdue.edu/prod/bwckschd.p_get_crse_unsec" arguements:nil];
if (!self.view.window) return;
NSArray *courseRecord = [PCFWebModel parseData:webData type:3];
PCFCourseRecord *record = [courseRecord objectAtIndex:0];
dispatch_async(dispatch_get_main_queue(), ^{
[[cell remainingActivityIndicator] stopAnimating];
[[cell staticAvailable] setHidden:NO];
NSString *val = #"1";
if ([val compare:record.enrolled] > 0) {
//[[cell available] setTextColor:[UIColor redColor]];
}else {
//[[cell available] setTextColor:[UIColor colorWithRed:.0565442 green:.430819 blue:.0724145 alpha:1]];
}
[[cell available] setText:[NSString stringWithFormat:#"SLOTS: %#/%#", record.enrolled,record.capacity]];
[[cell available] setHidden:NO];
[PCFAnimationModel fadeTextIntoView:cell.available time:1];
[PCFAnimationModel fadeTextIntoView:cell.staticAvailable time:1];
});
});
*/
// Configure the cell...
//[PCFFontFactory convertViewToFont:cell];
[cell.courseTitle setFont:[PCFFontFactory droidSansFontWithSize:13]];
[cell.courseName setFont:[PCFFontFactory droidSansFontWithSize:38]];
[cell.courseDaysOffered setFont:[PCFFontFactory droidSansBoldFontWithSize:11]];
NSNumber *state = [cellExpansionArray objectAtIndex:indexPath.section];
if (state.intValue == COLLAPSED) {
[cell.imageViewBackground setHidden:YES];
}else {
//[cell.imageViewBackground setHidden:NO];
}
return cell;
}
This looks like an autosizing issue to me! Subviews of your cell have flexible margins & width,height. I can give few suggestions and not the exact solution. Hope they help..
Try removing the autosizing properties to subviews (In XIB click on view->goto Size inspector->Autosizing) OR
Create 2 different cell templates to be used during states expand and collapse. Same Cell XIB with 2 different cell views. Use UINib to load the xib.
Dont use pattern image as cell background if image is fixed size. Make use of stretchableImageWithLeftCapWidth:topCapHeight image API.
Cheers!
Amar.
Not sure if this will help your situation. But often if you want to animate reloading cells in a table after changing the height without doing reloads and suchlike, you can just do the following.
[tableview beginUpdates];
[tableview endUpdates];
You haven't posted your cellForRowAtIndexPath but I am guessing you are wrongly reusing cells there. Check the answer to a similar question:
Objective C UITableView - Table cells display wrong content after changing cell's height
I tried with a code similar to yours and it seems to work as expected.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
states = [[NSMutableArray alloc] init];
for (int i = 0; i < 10; i++)
{
[states addObject:[NSNumber numberWithBool:false]];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 10;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellId"];
cell.textLabel.text = [NSString stringWithFormat:#"Title #%d", indexPath.section];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Subtitle #%d", indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
NSNumber *state = [states objectAtIndex:indexPath.section];
[states replaceObjectAtIndex:indexPath.section withObject:[NSNumber numberWithBool:!state.boolValue]];
[self.tableView reloadData];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *state = [states objectAtIndex:indexPath.section];
if (state.boolValue == false)
{
return 100;
}
else
{
return 244;
}
}
Related
I'm facing a crazy issue which is about expanding and shrinking UITableViewCell. When I click just one cell it works properly but while one cell remains open, clicking another cell to open causes the cell disorder. I would want to expand and shrink at the same time.
Here is my source.
- (void)viewDidLoad {
[super viewDidLoad];
//Initialize the array.
listOfItems = [[NSMutableArray alloc] init];
//Add items
[listOfItems addObject:#"1"];
[listOfItems addObject:#"2"];
[listOfItems addObject:#"3"];
[listOfItems addObject:#"4"];
[listOfItems addObject:#"5"];
[listOfItems addObject:#"6"];
[listOfItems addObject:#"7"];
[listOfItems addObject:#"8"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listOfItems count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UILabel *aLabel; UILabel *bLabel; UILabel *v1Label; UILabel *v2Label;; UIView *v1; UIView *v2;
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSLog(#" cell null");
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
aLabel = [[[UILabel alloc] initWithFrame:CGRectMake(9.0, 8.0, 50.0, 20.0)] autorelease];
aLabel.tag = 1;
aLabel.font = [UIFont systemFontOfSize:30];
[cell.contentView addSubview:aLabel];
v1 = [[UIView alloc] initWithFrame:CGRectMake(0, 44, 320, 116)];
v1.backgroundColor = [UIColor redColor];
v1.tag = 10;
v1.hidden = YES;
[cell.contentView addSubview:v1];
[v1 release];
v2 = [[UIView alloc] initWithFrame:CGRectMake(0, 44, 320, 100)];
v2.backgroundColor = [UIColor blueColor];
v2.tag = 11;
v2.hidden = YES;
[cell.contentView addSubview:v2];
[v2 release];
}
else {
aLabel = (UILabel *)[cell.contentView viewWithTag:1];
v1 = (UIView *) [cell.contentView viewWithTag:10];
v2 = (UIView *) [cell.contentView viewWithTag:11];
}
aLabel.text = [listOfItems objectAtIndex:indexPath.row];
if (SelectedIndexPath == indexPath.row)
{
if ([aLabel.text intValue] % 2) {
v1.hidden = NO;
v2.hidden = YES;
}
else {
v1.hidden = YES;
v2.hidden = NO;
}
}
else {
v1.hidden = YES;
v2.hidden = YES;
}
return cell;
}
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (SelectedIndexPath == indexPath.row)
{
return 100.0;
}
else {
return 46.0;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (SelectedIndexPath == -1)
{
OldSelectedIndexPath = indexPath.row;
SelectedIndexPath = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:NO];
}
else
{
if (SelectedIndexPath == indexPath.row)
{
OldSelectedIndexPath = SelectedIndexPath;
SelectedIndexPath = -1;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:NO];
}
else
{
SelectedIndexPath = indexPath.row;
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:[NSIndexPath indexPathForRow:OldSelectedIndexPath inSection:indexPath.section], indexPath, nil] withRowAnimation:NO];
OldSelectedIndexPath = SelectedIndexPath;
}
}
}
The problem comes from the facth that your cell will have a height of 160.0 when selected.
Look here
v1 = [[UIView alloc] initWithFrame:CGRectMake(0, 44, 320, 116)];
You give an heigh of 116 to a view which y origin is 44. Thus your cell view will have a total height of 160.0
Change the value returned in
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
To match the correct total height of your cell.
The problem is in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Or may be the problem is due to releasing. anything is going to release and you are still using it.
first comment // all release and inter prate your
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
I have created a custom UITableViewCell which I want to dynamically expand and contract when a button is press on the cell. The method is called when the button is pressed but it would appear that the cell is getting created twice.... Therefore resetting the state... I have been pouring over this for days trying different things but am at a loss as to what is wrong...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CategoryCell";
// Create a new Cell if necessary
CategoryCell *cell = (CategoryCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[CategoryCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.frame = CGRectMake(0.0, 0.0, 320.0, 67.0);
NSLog( #"Cell Creation - Row %d", indexPath.row );
[cell.expandContractButton addTarget:self action:#selector(expandContractButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
else {
NSLog( #"Cell Found - Row %d", indexPath.row );
}
// Setup the cell ...
Log....
2012-03-24 11:44:02.158 Review Writer[13523:fb03] Number of rows 1
2012-03-24 11:44:02.172 Review Writer[13523:fb03] Cell Creation - Row 0
2012-03-24 11:44:02.192 Review Writer[13523:fb03] Row 0 - setting height
2012-03-24 11:44:02.197 Review Writer[13523:fb03] Cell Creation - Row 0
Any pointers as to why this would be the case ?
Code to populate the cell data
-(void)tableView:(UITableView *)tableView willDisplayCell:(CategoryCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Setting up cell on Row %d - Expanded - %#", indexPath.row, cell.expanded ? #"YES" : #"NO");
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
Category *cat = [categoryList objectAtIndex:indexPath.row];
cell.categoryLabel.text = cat.category;
cell.ratingLabel.text = cat.overall_rating;
if (cell.expanded == YES)
{
[cell expandCell];
}
else {
[cell collapseCell];
}
cell.reviewText.text = cat.review_text;
if([cat.overall_rating isEqualToString:#"Not Rated"]){
[cell.ratingImage setImage:[UIImage imageNamed: #"0_stars.png"]];
}
if([cat.overall_rating isEqualToString:#"Unsatisfactory"]){
[cell.ratingImage setImage:[UIImage imageNamed: #"1_stars.png"]];
}
if([cat.overall_rating isEqualToString:#"Needs improvement"]){
[cell.ratingImage setImage:[UIImage imageNamed: #"2_stars.png"]];
}
if([cat.overall_rating isEqualToString:#"Meets job requirements"]){
[cell.ratingImage setImage:[UIImage imageNamed: #"3_stars.png"]];
}
if([cat.overall_rating isEqualToString:#"Exceeds job requirements"]){
[cell.ratingImage setImage:[UIImage imageNamed: #"4_stars.png"]];
}
if([cat.overall_rating isEqualToString:#"Outstanding"]){
[cell.ratingImage setImage:[UIImage imageNamed: #"5_stars.png"]];
}
// Put the accessory disclosure button on the cell
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
Code to set the height of the cell...
- (CGFloat) tableView: (UITableView *) tableView heightForRowAtIndexPath: (NSIndexPath *) indexPath
{
// Use the method that you would have created above to get the cell.
CategoryCell *cell = (CategoryCell*)[self tableView:tableView cellForRowAtIndexPath:indexPath];
NSString *text = cell.reviewText.text;
// Note the 30 is a fudge factor... :-) Otherwise it does not include the last line...
CGFloat width = [cell.reviewText frame].size.width - 30;
CGFloat height = [text sizeWithFont:cell.reviewText.font constrainedToSize:CGSizeMake(width, 9999) lineBreakMode:UILineBreakModeWordWrap].height;
NSLog( #"Row %d - setting height - Expanded = %#", indexPath.row, cell.expanded ? #"YES" : #"NO" );
if (cell.expanded == NO)
{
height = 0;
}
return height + 67;
}
Code to expand / collapse the cell ...
- (IBAction) expandContractButtonPressed:(id) sender{
CategoryCell *clickedCell = (CategoryCell *)[[sender superview] superview];
NSIndexPath *clickedButtonPath = [self.tableView indexPathForCell:clickedCell];
CategoryCell *cell = (CategoryCell*)[self tableView:self.tableView cellForRowAtIndexPath:clickedButtonPath];
NSLog( #"EXPAND / COLLAPSE Row %d Exanded = %#", clickedButtonPath.row, clickedCell.expanded ? #"YES" : #"NO");
if (clickedCell.expanded == YES)
{
NSLog( #"Collapse" );
[cell collapseCell];
clickedCell.expanded = NO;
}
else {
NSLog( #"Expand" );
[cell expandCell];
clickedCell.expanded = YES;
}
NSLog( #"Expanded after = %#", clickedCell.expanded ? #"YES" : #"NO" );
NSArray *indexPaths = [[NSArray alloc] initWithObjects:clickedButtonPath, nil];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:NO];
}
Only a guess, since you have provided only small parts of your code: maybe you have more than one section. You only check the row, but not the section, thus the code can be called for each section.
Nevertheless, your code should still be able to handle this situation (multiple creation) and properly show the cell.
You didn't show us the code for setting the height and content of the cell.
You should be o.k. if you keep only cell creation code in cellForRowAtIndexPath:
and move all cell setup code in willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath.
I am using these following codes to display multiple images on a table view. One row need to display 4 only images
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
//Resize auto complete table based on how many elements will be displayed in the table
return (int)ceil([wordsInSentence count]/4.0);
}
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"entered forRowAtIndexPath");
if ((indexPath.row % 2) == 0) {
cell.backgroundColor = [UIColor whiteColor];
}
else {
cell.backgroundColor = [UIColor whiteColor];
}
}
On the button click wich will display table view I wrote the below code
-(IBAction)setImageOnTableView
{
NSLog(#"entered setImageOnTableView");
j=1;
tableView.hidden=NO;
//Breaking the sentence into words and then placing it in an array
wordsInSentence=[[youSaid.text uppercaseString] componentsSeparatedByString:#" "];
[wordsInSentence retain];
[youSaid resignFirstResponder];
[tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
NSLog(#"entered cellForRowAtIndexPath");
//j=1;
static NSString *AutoCompleteRowIdentifier = #"AutoCompleteRowIdentifier";
cell = [tableView dequeueReusableCellWithIdentifier:AutoCompleteRowIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:AutoCompleteRowIdentifier] autorelease];
[cell.contentView addSubview:nil];
for (int i=0; i <= [wordsInSentence count]; ++i) {
UIImageView *imageView1 = [[[UIImageView alloc] initWithFrame:CGRectMake(30+90*(i%4), 20, 70, 100)] autorelease] ;
imageView1.tag = i+1;
[imageViewArray insertObject:imageView1 atIndex:i];
[cell.contentView addSubview:imageView1];
//[imageView1 release];
}
}
int photosInRow;
if ( (indexPath.row < [tableView numberOfRowsInSection:indexPath.section] - 1) || ([wordsInSentence count] % 4 == 0) ) {
photosInRow = 4;
} else {
photosInRow = [wordsInSentence count] % 4;
}
for ( int i = 1; i <=photosInRow ; i++ ){
imageView = (UIImageView *)[cell.contentView viewWithTag:j];
[self **showImage**:imageView];
}
return cell;
}
//method used to show the images on the table view by checking the category
//and images in the category
-(void)**showImage**:(UIImageView *)imageView
{
NSLog(#"entered showImage");
//this method will check if more than 1 image is present in the category
if([self searchImagesInSameCategory:[wordsInSentence objectAtIndex:j-1]]>1)
{
UIButton *descriptiveButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[descriptiveButton setFrame:CGRectMake(50, 0, 30, 30)];
[imageView setUserInteractionEnabled:TRUE];
[descriptiveButton addTarget:self action:#selector(showPopOver1:) forControlEvents:UIControlEventTouchUpInside];
descriptiveButton.tag=j;
[imageView addSubview:descriptiveButton];
globalString=[wordsInSentence objectAtIndex:j-1];
}
}
In this code when I displayed 4 images it will display 4. If I displayed 3 images after displaying 4 images, 3 images are displaying but still 4 th image is presenting in table view. How can I fix this. Please can any one help me...
The main problem is, an image view that displayed before is not removing from the cell.
Table cells can be (and in your code, are) cached and reused - which means that for every call to cellForRowAtIndexPath:: you should make sure that the cell you are returning is correct (has the correct images). In your case, I would simply clear the cell of all subviews/image views and recreate them for every fetch.
I am trying create an accordion type of uitableviewcell that, when the user selects the cell, it expands to display a detailed info view inline similar to how the digg app works. I initially tried replacing the current tablecell with a customcell in cellForRowAtIndex, however the animation looks a bit choppy as you can see the cell being replaced and overall the effect doesn't work too well.
If you look at the digg app and others who have done this it seems that they aren't replacing the current cell but instead perhaps adding a subview to the cell? The original cell however doesn't seem to animate at all and only the new view accordions into the table.
Does anyone have any ideas how to accomplish a similar effect?
I have made some progress using neha's method below and while the cell is animating the correct way it is wreaking havoc with the other cells in the table. What I have done is subclassed UITableViewCell with a custom class which contains an instance of a UIView which actually draws the view which I then add to the table cell's contentview.
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
if (selected) {
[self expandCell];
}
}
-(void)expandCell {
self.contentView.frame = CGRectMake(0.0, 0.0, self.contentView.bounds.size.width, 110);
}
Here are all the table delegate methods I am using:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (isSearching && indexPath.row == selectedIndex) {
static NSString *CellIdentifier = #"SearchCell";
CustomTableCell *cell = (CustomTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]];
UILabel *theText = [[UILabel alloc] initWithFrame:CGRectMake(10.0, 10.0, cell.contentView.bounds.size.width -20, 22.0)];
theText.text = #"Title Text";
[cell.contentView addSubview:theText];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0, 10 + 46.0, cell.contentView.bounds.size.width - 20, 40.0)];
textField.borderStyle = UITextBorderStyleLine;
[cell.contentView addSubview:textField];
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(5.0, 88.0, cell.contentView.bounds.size.width - 20, 22.0)];
testLabel.text = [NSString stringWithFormat:#"Some text here"];
[cell.contentView addSubview:testLabel];
[theText release];
[textField release];
[testLabel release];
return cell;
} else {
static NSString *CellIdentifier = #"Cell";
CustomTableCell *cell = (CustomTableCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
selectedIndex = indexPath.row;
isSearching = YES;
[tableView beginUpdates];
[tableView endUpdates];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (isSearching && indexPath.row == selectedIndex) {
return 110;
}
return rowHeight;
}
It seems now that the cell is expanding but not actually being refreshed so the labels, and textfield aren't being shown. They do however show up when I scroll the cell off and on the screen.
Any ideas?
The Apple way to do is quite simple.
First, you'll need to save the selected indexPath row:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedRowIndex = [indexPath retain];
[tableView beginUpdates];
[tableView endUpdates];
}
I'll explain the begin/end updated part later.
Then, when you have the currently selected index, you can tell the tableView that it should give that row more space.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//check if the index actually exists
if(selectedRowIndex && indexPath.row == selectedRowIndex.row) {
return 100;
}
return 44;
}
This will return height 100 for the selected cell.
Now we can go back to the begin/end updates. That block triggers the reload of all tableView geometry. Moreover, that block is animated, which eventually gives the impressions of the row expanding.
Pawel's beginUpdates/endUpdates trick is good, and I often use it. But in this case you simply need to reload the rows that are changing state, ensuring that you correctly reload them with the desired cell type, and that you return the correct new cell height.
Here is a complete working implementation of what I think you're trying to accomplish:
.h:
#import <UIKit/UIKit.h>
#interface ExpandingTableViewController : UITableViewController
{
}
#property (retain) NSIndexPath* selectedIndexPath;
#end
.m:
#implementation ExpandingTableViewController
#synthesize selectedIndexPath;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier1 = #"Cell1";
static NSString *CellIdentifier2 = #"Cell2";
UITableViewCell *cell;
NSIndexPath* indexPathSelected = self.selectedIndexPath;
if ( nil == indexPathSelected || [indexPathSelected compare: indexPath] != NSOrderedSame )
{
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier1] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat: #"cell %d", indexPath.row];
}
else
{
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier2];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier2] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat: #"cell %d", indexPath.row];
cell.detailTextLabel.text = [NSString stringWithFormat: #"(expanded!)", indexPath.row];
}
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ( self.selectedIndexPath != nil && [self.selectedIndexPath compare: indexPath] == NSOrderedSame )
{
return tableView.rowHeight * 2;
}
return tableView.rowHeight;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray* toReload = [NSArray arrayWithObjects: indexPath, self.selectedIndexPath, nil];
self.selectedIndexPath = indexPath;
[tableView reloadRowsAtIndexPaths: toReload withRowAnimation: UITableViewRowAnimationMiddle];
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
}
- (void)dealloc {
[super dealloc];
}
#end
If you don't want to reload the cell (you want to keep your existing cell and just change the size, and likely add/remove some subviews), then simply do the beginUpdates/endUpdates trick in didSelectRowAtIndexPath:, and call some method on your cell to incite the layout change. beginUpdates/endUpdates will prompt the tableView to re-query the heights for each cell - so be sure to return the correct value.
Create a class that subclasses UITableviewcell in your project. Create this class' nib and set its parent to be the class in your project with tableview and override its -
(void)setSelected:(BOOL)selected animated:(BOOL)animated
Write methods contractCell() and expandCell() in this class, and provide the height of the cells you want in expandCell method. Call this methods appropriately based on some flags set to identify wheather the cell is in expanded state or contracted state. Use your tableview's
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
method to handle selection of cells.
Replace your cellForRowAtIndexPath function with this one.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath {
if (isSearching && indexPath.row == selectedIndex) {
static NSString *CellIdentifier = #"SearchCell";
CustomTableCell *cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
[cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]];
UILabel *theText = [[UILabel alloc] initWithFrame:CGRectMake(10.0,
10.0, cell.contentView.bounds.size.width
-20, 22.0)];
theText.text = #"Title Text";
[cell.contentView addSubview:theText];
UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(10.0, 10 +
46.0, cell.contentView.bounds.size.width - 20, 40.0)];
textField.borderStyle = UITextBorderStyleLine;
[cell.contentView addSubview:textField];
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(5.0,
88.0, cell.contentView.bounds.size.width - 20, 22.0)];
testLabel.text = [NSString stringWithFormat:#"Some text here"];
[cell.contentView addSubview:testLabel];
[theText release];
[textField release];
[testLabel release];
return cell;
} else {
static NSString *CellIdentifier = #"Cell";
CustomTableCell *cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
[cell setCustomTitle:[timeZoneNames objectAtIndex:indexPath.row] detail:[timeZoneNames objectAtIndex:indexPath.row]];
return cell;
}
}
create array wof dictionary which have a key Select_sts which is 0 in start when click its change 1
accourding u change table
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
customView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 40.0)];
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor blackColor];
headerLabel.highlightedTextColor = [UIColor whiteColor];
headerLabel.font = [UIFont boldSystemFontOfSize:16];
headerLabel.frame = CGRectMake(5.0, 10.0, 300.0, 20.0);
headerLabel.text=[NSString stringWithFormat: #"PNR %#",[[record objectAtIndex:section] objectForKey:#"number"]];
customView.backgroundColor=[UIColor whiteColor];
btn_openClose.tag=section+10000;
btn_openClose.backgroundColor=[UIColor clearColor];
// [btn_openClose setImage:[UIImage imageNamed:#"down_arrow.png"] forState:UIControlStateNormal];
[btn_openClose addTarget:self action:#selector(collapseExpandButtonTap:) forControlEvents:UIControlEventTouchUpInside];
[customView addSubview:btn_openClose];
}
- (void) collapseExpandButtonTap:(id) sender{
int indexNo=[sender tag]-10000;
// NSLog(#"total_record %#",[total_record objectAtIndex:indexNo]);
NSMutableDictionary *mutDictionary = [[total_record objectAtIndex:indexNo] mutableCopy];
if([[mutDictionary objectForKey:#"Select_sts"] integerValue]==0)
[mutDictionary setObject:[NSNumber numberWithInt:1] forKey:#"√"];
else
[mutDictionary setObject:[NSNumber numberWithInt:0] forKey:#"Select_sts"];
[total_record replaceObjectAtIndex:indexNo withObject:mutDictionary];
// [table_view beginUpdates];
// [table_view reloadData];
// [table_view endUpdates];
NSMutableIndexSet *indetsetToUpdate = [[NSMutableIndexSet alloc]init];
[indetsetToUpdate addIndex:indexNo]; // [indetsetToUpdate addIndex:<#(NSUInteger)#>]
// You can add multiple indexes(sections) here.
[table_view reloadSections:indetsetToUpdate withRowAnimation:UITableViewRowAnimationFade];
}
I have a UITableView in an iPhone application which I am refreshing (by calling [self.tableView reloadData] in the action method for a UISegmentedControl dynamically embedded in one of the UITableView cells. The table view is refreshed to update a text value for one of the cells.
However, the following code seems to produce an unwanted side-effect. It appears that each time the UITableView refreshes it creates a new instance of the UISegmentedControl (and possibly the images - I'm not sure) over the existing one(s).
The only reason I notice this is that with each refresh a barely perceptible border starts to form around the UISegmentedControl and the application slows noticeably. I would be extremely grateful for any suggestions/code-solutions to my current predicament.
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
}
NSUInteger section = indexPath.section;
NSUInteger row = indexPath.row;
// Set up the cell...
//populates the personal info section
if (section == kPersonalInfoAddSection) {
if (row == kNameRow) {
//Other code irrelevant to this question was removed for the sake of clarity
}
else if(row == kHeightRow) {
cell.imageView.image = [UIImage imageNamed:#"tableview_height_label.png"];
//THIS IS THE TEXT I'M TRYING TO UPDATE
cell.textLabel.text = [Formatter formatHeightValue:mainUser.heightInMM forZone:self.heightZone];
cell.detailTextLabel.text = REQUIRED_STRING;
}
}
//populates the units section
if (section == kUnitsSection) {
if (row == kHeightUnitsRow) {
NSArray *heightUnitsSegments = [[NSArray alloc] initWithObjects:FT_AND_IN_STRING, M_AND_CM_STRING, nil];
UISegmentedControl *heightUnitControl = [[UISegmentedControl alloc] initWithItems:heightUnitsSegments];
CGRect segmentRect = CGRectMake(90, 7, 200, 30);
[heightUnitControl setFrame:segmentRect];
//[heightUnitControl setSelectedSegmentIndex:0];
[heightUnitControl addTarget:self action:#selector(heightSegmentClicked:) forControlEvents:UIControlEventValueChanged];
heightUnitControl.tag = kHeightSegmentedControlTag;
cell.textLabel.text = #"Height:";
cell.detailTextLabel.text = #"(units)";
[cell.contentView addSubview:heightUnitControl];
[heightUnitsSegments release];
[heightUnitControl release];
}
else if(row == kWeightUnitsRow) {
//Other code irrelevant to this question was removed for the sake of clarity
}
}
return cell;
}
Thank you all in advance!
You're right, it is creating a new instance of the UISegmentedControl. It's because you are using a generic cell identifier, #"Cell", then adding the UISegmentedControl each time, never removing it. The cells get cached containing the UISegmentedControl, you retrieve the cached cell and add the control again.
You could use a more specific cell identifier and if cell != nil you know it contains the UISegmentedControl already. Or create a new cell each time that way you're not using a cached cell that already contains the control.
With the image view you just set the cells image view property without adding a new view to the cell so that one is ok, it gets replaced each time.
Since the text you are trying to update doesn't have to do with the UISegmentedControl I think you should be able to use a more specific cell identifier and add the control only on cell creation.
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *arr1=[NSArray arrayWithObjects:#"img1.jpg",#"img2.jpg",nil];
NSArray *arr2=[NSArray arrayWithObjects:#"img1.jpg",#"img2.jpg",#"img3.jpg",#"img4.jpg",#"img5.jpg",#"img6.jpg",nil];
NSArray *arr3=[NSArray arrayWithObjects:#"img6.jpg",#"img5.jpg",#"img2.jpg",#"img1.jpg",nil];
Imgs = [[NSArray alloc] initWithArray:[NSArray arrayWithObjects:arr1,arr2,arr3,nil]];
NSDictionary *dic1=[NSDictionary dictionaryWithObjectsAndKeys:#"Ahmedabad",#"Name",#"Picture 5.png",#"Rating",#"Picture 1.png",#"Photo",arr1,#"img",nil];
NSDictionary *dic2=[NSDictionary dictionaryWithObjectsAndKeys:#"Rajkot",#"Name",#"Picture 5.png",#"Rating",#"Picture 2.png",#"Photo",nil];
NSDictionary *dic3=[NSDictionary dictionaryWithObjectsAndKeys:#"Baroda",#"Name",#"Picture 5.png",#"Rating",#"Picture 7.png",#"Photo",nil];
tblArray=[[NSArray alloc] initWithObjects:dic1,dic2,dic3,nil];
[myTbl reloadData];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.navigationController.navigationBarHidden=NO;
[self.navigationController.navigationBar setUserInteractionEnabled:YES];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
self.navigationController.navigationBarHidden=YES;
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [tblArray count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *CellIdentifer=[NSString stringWithFormat:#"%i",indexPath.row];
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:CellIdentifer];
if(cell==nil){
cell=[self myCustomCell:CellIdentifer dicToSet:[tblArray objectAtIndex:indexPath.row]];
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
}
return cell;
}
-(UITableViewCell*)myCustomCell:(NSString*)CellIdentifer dicToSet:(NSDictionary*)dicToSet{
UITableViewCell *cell=[[[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, 320, 44) reuseIdentifier:CellIdentifer] autorelease];
UIImageView *imgV=[[UIImageView alloc] initWithFrame:CGRectMake(2, 2, 40, 40)];
[imgV setImage:[UIImage imageNamed:[dicToSet valueForKey:#"Photo"]]];
[cell addSubview:imgV];
[imgV release];
UILabel *lbl=[[UILabel alloc] initWithFrame:CGRectMake(44, 2, 276, 20)];
[lbl setText:[dicToSet valueForKey:#"Name"]];
[cell addSubview:lbl];
[lbl setBackgroundColor:[UIColor clearColor]];
[lbl setFont:[UIFont fontWithName:#"Helvetica-Bold" size:18]];
[lbl release];
UIImageView *imgV1=[[UIImageView alloc] initWithFrame:CGRectMake(44, 24, 70, 20)];
[imgV1 setImage:[UIImage imageNamed:[dicToSet valueForKey:#"Rating"]]];
[cell addSubview:imgV1];
[imgV1 release];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
nxtPlcDtl=[[plcFullDtl alloc] initWithNibName:#"plcFullDtl" bundle:nil];
nxtPlcDtl.dict=[[NSDictionary alloc] initWithDictionary:[tblArray objectAtIndex:indexPath.row]];
nxtPlcDtl.Imgs = [Imgs objectAtIndex:indexPath.row];
nxtPlcDtl.comment1 = [comment1 objectAtIndex:indexPath.row];
nxtPlcDtl.vedio = [vedio objectAtIndex:indexPath.row];
[self.navigationController pushViewController:nxtPlcDtl animated:YES];
}