I have a UITableView with a special row at the end to insert a new item. That works, but I want it to have the green plus icon without having to put the table view into editing mode. How can I do that?
I'd prefer not to create a button or bundle the image, if possible. Is there any way to do either or both of those things using only standard UITableView/UITableViewCell features?
you want to set the accessoryView to the cell:
#interface RootViewController : UITableViewController {
NSInteger nextValue;
NSMutableArray *timeIntervals;
}
#implementation RootViewController
- (NSNumber *)nextValue {
NSNumber *n = [NSNumber numberWithInteger:nextValue];
nextValue++;
return n;
}
- (void)viewDidLoad {
[super viewDidLoad];
nextValue = 1;
timeIntervals = [[NSMutableArray alloc] init];
[timeIntervals addObject:[self nextValue]];
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return [timeIntervals count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"TimeIntervalCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier] autorelease];
UIButton *b = [UIButton buttonWithType:UIButtonTypeContactAdd];
[b addTarget:self action:#selector(addTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryView = b;
}
NSNumber *number = [timeIntervals objectAtIndex:indexPath.row];
cell.accessoryView.tag = indexPath.row;
cell.textLabel.text = [number stringValue];
cell.detailTextLabel.text = #"detail about this number";
return cell;
}
- (void)addTapped:(UIButton *)sender {
id cell = sender;
while (cell != nil && [cell isKindOfClass:[UITableViewCell class]] == NO)
cell = [cell superview];
if (cell == nil) {
NSLog(#"[%# %#] sender was not in a cell",
NSStringFromClass([self class]), NSStringFromSelector(_cmd));
return;
}
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSInteger index = indexPath.row + 1; // insert after current cell
[timeIntervals insertObject:[self nextValue] atIndex:index];
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"[%# %#] not implemented", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
}
#end
(this is all the modified code to the Xcode 4.0.2 navigation app template)
You could implement your last cell as Custom Cell and add the green icon as per your choice.
See the tutorial to implement custom cell.
iPhone Programming Tutorial: Part 6: Creating custom UITableViewCell Using Interface Builder UITableView
Updated:
Let's say cell is the instance of UITabelViewCell.
First create a button using your green icon.
UIButton myGreenIconButton = [UIButton buttonWithType:UIButtonTypeCustom];
[myGreenIconButton addTarget:self action:#selector(GreenIconButtonClicked:)forControlEvents:UIControlEventTouchUpInside];
[myGreenIconButton setBackgroundImage:[UIImage imageNamed:#"greenIcon.png"] forState:UIControlStateNormal];
myGreenIconButton.tag = i;
myGreenIconButton.backgroundColor = [UIColor clearColor];
myGreenIconButton.frame = CGRectMake(5, 78, 15, 15);
Now add it as subview in your last UITabelViewCell.
[cell addSubview:myGreenIconButton];
Implement GreenIconButtonClicked: method to receive the click evrnt on you add green icon button
-(void) GreenIconButtonClicked:(id) sender
{
}
Unfortunately, the only way I've found to do this is to set the cell's image, which means you have to handle the image files yourself instead of having UIKit load them for you. I recommend using the UIKit Artwork Extractor to get the image.
Related
I am new to iphone.I am struck in my project at some task (i.e),I have a table view with 66 rows.In that i am placed different book names for each cell and place a download button to each book.My requirement is when we click on download button it shows the progress view in that particular cell only but i am getting in that particular cell but when i am drag the tableview it will shows the progress views in some that cells also.It is because of dequeue reusability concept but i dont know how to avoid this problem.I want even after drag the tableview it shows the progress view on the cell which i am click the download button (cell)
here is my code below..
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 66;
}
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UIButton *downloadButton = nil;
CustomCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
//here custom cell is another class in that we have the title label declaration
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
downloadButton = [UIButton buttonWithType:UIButtonTypeCustom];
downloadButton.frame = CGRectMake(220,10,50,30);
[downloadButton setImage:[UIImage imageNamed:#"download.png"] forState:UIControlStateNormal];
[downloadButton addTarget:self action:#selector(downloadButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
downloadButton.backgroundColor = [UIColor clearColor];
downloadButton.userInteractionEnabled = YES;
downloadButton.highlighted = YES;
downloadButton.tag = indexPath.row;
NSLog(#"tag is %d",indexPath.row);
[cell.contentView addSubview:downloadButton];
}
NSString *titleLabel = [[appDelegate getBookNames]objectAtIndex:indexPath.row];
cell.TitleLabel.text = titleLabel;
return cell;
}
-(void)downloadButtonClicked:(id)sender{
int index = [sender tag];
NSLog(#"index of the cell is %d",index);
UIButton *button = (UIButton*)sender;
UITableViewCell *cell = (UITableViewCell *)[[button superview] superview];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:100];
NSLog(#"label text =%#",titleLabel.text);
selectedBookTitle = titleLabel.text;
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSMutableArray *allDownloadLinks;
biblePlayerViewController = [[BiblePlayerViewController alloc]init];
allDownloadLinks = [biblePlayerViewController allDownloadLinks];
NSLog(#"all Download Links are %#",allDownloadLinks);
biblePlayerViewController.indexOfSelectedBookTitle = [[appDelegate getBookNames]indexOfObject:selectedBookTitle];
Download* download = [Download downloadWithTitle:selectedBookTitle url:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.audiotreasure.com/%#.zip",[allDownloadLinks objectAtIndex:(biblePlayerViewController.indexOfSelectedBookTitle)]]]PathtoSave:documentsPath];
[[DownloadManager sharedDownloadManager] queueDownload: download];
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
progressView.frame = CGRectMake(10, 40, 300, 20);
[tableViewCell.contentView addSubview:progressView];
}
screen shot of my project is [output of my above code which is in simulator]
you should nil the cell every time in cellForRow. This way it will not be re-used and allocated every time. It should work pretty fine in your case as your tableview is not very large. Just add the following line before cell == nil check:
cell = nil;
It should work now.
I have the same problem, one way to avoid it is to just lock the scrolling ability of the table during the download.
I am new to iphone.I have a small question that is,I have a class CustomCell in that i have declare all elements which i have required like the below
// CustomCell.h
here in this class i am setting the getter and setter methods to TitleLabel, PercentageLabel, ProgressView, downloadButton
// CustomCell.m
here in this class set the frame to above all,and set the image to the download button also here
screen shot of my output screen is
// In my table view i have a download button when we click that the following method will executes
-(void)downloadButtonClicked:(id)sender
{
NSLog(#"download button clicked");
int index = [sender tag];
NSLog(#"index of the cell is %d",index);
UIButton *button = (UIButton*)sender;
UITableViewCell *cell = (UITableViewCell *)[[button superview] superview];
UILabel *titleLabel = (UILabel *)[cell viewWithTag:100];
NSLog(#"label text =%#",titleLabel.text);
UIView *senderButton = (UIView*) sender;
NSIndexPath *indexPath = [tableView indexPathForCell: (UITableViewCell*)[[senderButton superview]superview]];
NSLog(#"indexpath is %d",indexPath.row);
CustomCell *customCell = [[CustomCell alloc]init];
progressView = [[UIProgressView alloc]init];
// [customCell.contentView insertSubview:progressView atIndex:indexPath.row];
[customCell.contentView addSubview:progressView];
selectedBookTitle = titleLabel.text;
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSMutableArray *allDownloadLinks;
biblePlayerViewController = [[BiblePlayerViewController alloc]init];
allDownloadLinks = [biblePlayerViewController allDownloadLinks];
NSLog(#"all Download Links are %#",allDownloadLinks);
biblePlayerViewController.indexOfSelectedBookTitle = [[appDelegate getBookNames]indexOfObject:selectedBookTitle];
Download* download = [Download downloadWithTitle:selectedBookTitle url:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.audiotreasure.com/%#.zip",[allDownloadLinks objectAtIndex:(biblePlayerViewController.indexOfSelectedBookTitle)]]]PathtoSave:documentsPath];
[[DownloadManager sharedDownloadManager] queueDownload: download];
}
// In cellForRowAtIndexPath tableview delegate method I have written code like this
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = [_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSLog(#"indexpath.row is %d",indexPath.row);
NSLog(#"downloadmanager is %d",[[[DownloadManager sharedDownloadManager]getDownloads]count]);
/* if (indexPath.row<[[[DownloadManager sharedDownloadManager] getDownloads]count])
{
cell.TitleLabel.text = ((Download*)[[[DownloadManager sharedDownloadManager] getDownloads] objectAtIndex:indexPath.row]).title_;
cell.ProgressView.progress = (float)(((Download*)[[[DownloadManager sharedDownloadManager] getDownloads] objectAtIndex:indexPath.row]).PercentageDownloaded_ )/100.0f;
cell.PercentageLabel.text = [NSString stringWithFormat:#"%d %%",((Download*)[[[DownloadManager sharedDownloadManager] getDownloads] objectAtIndex:indexPath.row]).PercentageDownloaded_];
}
else
{
[tableView reloadData];
}*/
NSString *titleLabel = [[appDelegate getBookNames]objectAtIndex:indexPath.row];
cell.TitleLabel.text = titleLabel;
cell.downloadButton.tag = indexPath.row;
NSLog(#"tag is %d",cell.downloadButton.tag);
[cell.downloadButton addTarget:self action:#selector(downloadButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
My question is I have 66 cells in tableview in screen shot we have a bookname called Revelation.It has a 66th cell in my tableview (index is 65).Here when i click the download button in that cell,I have to show progress view in that particular cell only (65th cell).I have reload the table view for every 1 sec also because after placing the progress view we have to show the progress in that progress view in that cell.So, how can we place a progress view in that particular cell index.
Why don't you add the progress view in the cell from the beginning?
have it initially hidden and when you touch the download button you tell the cell to show it.
i see that you store the indexPath.row of the cell in the download button tag. You could use that to find out what cell was selected something like below.
NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:sender.tag inSection:0];
CustomCell *cell = (CustomCell *)[tableView cellForRowAtIndexPath:nowIndex];
[cell showProgressBar];
i wrote the code of top of my head..didn't tested it.
I have a tableViewController, the cells contains a button and a label. I need to get the text of the cell (actually the object of the cell Person) when the user clicks on the button.
When the user clicks on the button the following method gets executed;
-(void) buttonOfCellClicked{
// here i need to access the `Person` object that the user clicked
}
How do i write this code?
EDIT:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Person *person = [personsArr objectAtIndex:indexPath.row];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] ;
label = [[UILabel alloc]initWithFrame:CGRectMake(15, 5, 75, 60)];
label.text =person.firstName;
[cell addSubview:label];
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(buttonOfCellClicked) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
}
id findAncestor(UIView *view, Class class) {
while (view && ![view isKindOfClass:class])
view = [view superview];
return view;
}
- (void)buttonOfCellClicked:(UIButton *)button
{
UITableViewCell *cell = (UITableViewCell *)findAncestor(button, [UITableViewCell class]);
UITableView *table = (UITableView *)findAncestor(cell, [UITableView class]);
NSIndexPath *path = [table indexPathForCell:cell];
if (!path)
return;
Person *person = [personsArr objectAtIndex:path.row];
// do whatever with person
}
I would subclass UITableviewCell and add a property and the action to that class and in IB connect the button. When you are configuring the cell just set the person object. This approach would depend heavily on what you plan on doing with that object.
You need to ask your button to tell you where it is. To achieve this, you need to call superview twice (1x will return cell.contentView, 2x will return cell):
- (void)buttonOfCellClicked:(UIButton *)sender {
UITabvleViewCell *cell = (UITableViewCell *)[[sender superview] superview];
}
Then you can access cell's properties e.g. cell.textLabel.text
EDIT: You also need to add : to your -addTarget:action:forControlEvents: in cellForRow after buttonOfCellClicked text:
[button addTarget:self action:#selector(buttonOfCellClicked:) forControlEvents:UIControlEventTouchUpInside]
EDIT2: you can access person by
NSIndexPath *ip = [yourTableView indexPathForCell:cell];
Person *p = [personArr objectAtIndex:ip.row];
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [yourArray count];\
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] ;
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTag:indexPath.row]; // SET TAG TO YOUR BUTTON, IT WILL BE SAME AS YOUR OBJECT AT INDEX OF ARRAY
[button addTarget:self action:#selector(buttonOfCellClicked:) forControlEvents:UIControlEventTouchUpInside];
[cell.contentView addSubview:button];
}
-(void) buttonOfCellClicked:(id) sender{
Person *person = [yourArray objectAtIndex:[sender tag]];
// here i need to access the `Person` object that the user clicked
}
I have a bit of a problem where my check marks that i apply to my rows in my UITableView get all mixed up when i scroll. Im pretty sure this has to do with how the iphone reuses the cells and when i scroll away from on that has a check mark it probably puts it back in when i gets a chance.
Could someone please give me some tips on how I might avoid this or possibly take a look at my methods and see if anything looks off?
I was thinking that maybe I could save each row selection that the user made and then check to see which rows were being displayed to make sure the correct ones got the checkmark but I could'nt see a way to do so.
Thanks so much.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
[cell setAccessoryView:nil];
}
NSMutableArray *temp = [[NSMutableArray alloc]init];
for (int j = 0; j < [listOfRowersAtPractice count]; j++) {
if ([[differentTeams objectAtIndex:indexPath.section] isEqualToString:[[rowersAndInfo objectForKey:[listOfRowersAtPractice objectAtIndex:j]]objectForKey:#"Team"]]) {
[temp addObject:[listOfRowersAtPractice objectAtIndex:j]];
}
}
[cell.cellText setText:[temp objectAtIndex:indexPath.row]];
[temp removeAllObjects];
[temp release];
// Set up the cell...
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType != UITableViewCellAccessoryCheckmark) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}
Yes save the state of the row which is selected and in cellforrowatindexpath after you get the cell reset it to default state and check the state of the row and change the state.
EDIT:
You can create a NSMutabaleArray with number of items equal to the number of items in your datasource which is the name temp in your code.
On select you can actually change the value at that index to some text like #"selected" in the above created array.
In your cellforrowatindexpath you can check this text if its selected or unselected and then change the property of the cell. Its like maintaining a bitmap state for selected and unselected states.
Give this a go :
static NSString *CellIdentifier = [NSString stringWithFormat:#"Cell %d",indexPath.row];
I had the same problem on one of my app's.
As for the check marks, are you using a core data store at all?
If you are use the following....
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *item = [[self fetchedResultsController] objectAtIndexPath:indexPath];
if ([[item valueForKey:#"checks"] boolValue]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[cell.textLabel setTextColor:[UIColor redColor]];
[cell.detailTextLabel setTextColor:[UIColor redColor]];
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
[cell.textLabel setTextColor:[UIColor blackColor]];
[cell.detailTextLabel setTextColor:[UIColor blackColor]];
}
}
And......
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSManagedObject *selectedObject = [self.fetchedResultsController objectAtIndexPath:indexPath];
if ([[selectedObject valueForKey:#"checks"] boolValue]) {
[selectedObject setValue:[NSNumber numberWithBool:NO] forKey:#"checks"];
} else {
[selectedObject setValue:[NSNumber numberWithBool:YES] forKey:#"checks"];
}
[managedObjectContext save:nil];
}
You need to reset/clear all settings in the cell whenever you reuse the cell.
So here, right after you get the cell,
you need to do something like
CustomCell *cell = (CustomCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[CustomCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
[cell setAccessoryView:nil];
}
cell.accessoryType = UITableViewCellAccessoryNone // This and other such calls to clean up the cell
You need refresh the accessoryType of cell, because the cell is reused then it inherited the accessoryType from a reused Cell, this is the solution:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"cellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
//Refresh acessory for cell when tableview have many cells and reuse identifier
if([self.tableView.indexPathsForSelectedRows containsObject:indexPath]){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}else{
cell.accessoryType = UITableViewCellAccessoryNone;
}
cell.textLabel.text = #"Your text cell";
return cell;
}
it worked for me..
in cell for row at index path i had created a checkbox button..
after everytym tableview is scrolled cellForRowAtIndexPath Method gets called
hence i had to add condition in cellForRowAtIndexPath to check whether a cell has a checked or unchecked button
static NSString *simpleTableIdentifier = #"SimpleTableCell";
SimpleTableCell *cell = (SimpleTableCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"SimpleTableCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
cell.nameLabel.text = [tableData objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[thumbnails objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [prepTime objectAtIndex:indexPath.row];
checkbox = [[UIButton alloc]initWithFrame:CGRectMake(290, 5, 20, 20)];
[checkbox setBackgroundImage:[UIImage imageNamed:#"checkbox_empty.png"]
forState:UIControlStateNormal];
[checkbox addTarget:self action:#selector(checkUncheck:) forControlEvents:UIControlEventTouchUpInside];
[cell addSubview:checkbox];
if(selectedRows.count !=0)
{
if([[selectedRows objectAtIndex:indexPath.row]integerValue]==1)
{
[checkbox setImage:[UIImage imageNamed: #"checkbox_full.png"] forState:UIControlStateNormal];
}
else
{
[checkbox setImage:[UIImage imageNamed: #"checkbox_empty.png"] forState:UIControlStateNormal];
}
}
return cell;
}
method to define selection of checkbox is as
- (IBAction)checkUncheck:(id)sender {
UIButton *tappedButton = (UIButton*)sender;
NSLog(#"%d",tappedButton.tag);
if ([[sender superview] isKindOfClass:[UITableViewCell class]]) {
UITableViewCell *containerCell = (UITableViewCell *)[sender superview];
NSIndexPath *cellIndexPath = [self.tableView indexPathForCell:containerCell];
int cellIndex = cellIndexPath.row;
NSLog(#"cell index%d",cellIndex);
[selectedRows insertObject:[NSNumber numberWithInt:1] atIndex:cellIndex];
}
NSLog(#"%#",selectedRows);
if([tappedButton.currentImage isEqual:[UIImage imageNamed:#"checkbox_empty.png"]])
{
[sender setImage:[UIImage imageNamed: #"checkbox_full.png"] forState:UIControlStateNormal];
}
else
{
[sender setImage:[UIImage imageNamed: #"checkbox_empty.png"] forState:UIControlStateNormal];
}
}
do not forget to initialize selectedRows array..
happy coding...!!!
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];
}