I can't seem to get dequeueReusableCellWithIdentifier working.
I need to build a project for IOS 4 so I can't use storyboards, but I'm using ARC.
Let's say I have 2 sections, each with 1 row.
Looking at the code below, I'm using the strong property to pass ownership, since ARC would insert the "autorelease" code.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"TableCellIdentifier";
MainTableCell *cell = (MainTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
self.retainedCell = [[MainTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
However, for each row, cell is always nil (and hence a new MainTableCell is alloc'd) each time the function is called. The cell is never re-used.
This wouldn't be so much a problem except I programmatically call tableView:cellForRowAtIndexPath:, meaning I get a newly alloc'd cell each time, rather than the existing cell.
The only method I can see is to add the cells to an NSMutableArray.
Is there something I'm missing with dequeueReusableCellWithIdentifier now?
Thanks!
EDIT
I'm using the code below to get the cell. As mentioned it's creating a new cell not re-using the one that should have been already made + retained.
I don't need to invoke reloadData for all the rows, just change a specific one.
MainTableCell *cell = (MainTableCell *)[self tableView:self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
[self configureCell:cell atIndexPath:indexPath];
You happen to be de-queuing MainTableCell, and then you proceed to check if it is nil, at which point you use a completely different var to alloc a table cell. What the heck? Try this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"TableCellIdentifier";
MainTableCell *cell = (MainTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[MainTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
Related
just created an UITableView,
trying to display some custom data from an array, but what ever I do, i get no text displayed.
NSLog tell me the right text and right amout but no text in table cell.
here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"test";
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
Things to check:
Is cellForRowAtIndexPath getting called? Put a breakpoint in
Make an array in ViewDidLoad (property, alloc'd) and addObjects #"One", #"Two", #"Three", nil and then cell.textLabel.text = [theArray objectAtIndex:indexPath.row];
IfcellForRow is being called, this will show in the cells.
What doesConfigureCell do? Include the code please.
Also check your tableView delegate methods are being called (NumberofRowsInSection etc)
Always set cell properties (text, images, accessory views, etc) in the
tableView:willDisplayCell:forRowAtIndexPath
delegate method.
UITableView sometimes send a prepareForReuse to the cell after it is returned from
tableView:cellForRowAtIndexPath
which causes the cell to reset it's labels and images.
I am using ios5 storyboards with a UITableViewController with a UITableViewCell subclass. I do not want to design the visual elements of the cell in the storyboard designer for the view, because I want to use a reusable subclass of UITableViewCell (specifically TDBadgedCell).
I have set my cell identifier in the storyboard designer, and all of the rows load correctly in the UITableView as long as I'm not setting any of the properties unique to TDBadgedCell. If I set the badgeString property though which is unique to TDBadgedCell, I get an exception. I narrowed down that dequeueReusableCellWithIdentifier: is not returning a cell of type TDBadgedCell.
I'm only running into this with a UITableViewController. I have a UIViewController with an embedded UITableView set up in the same fashion and it's not an issue. Any ideas?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath
{
static NSString *CellIdentifier = #"PhoneNumberCell";
TDBadgedCell *cell = (TDBadgedCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[TDBadgedCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
if ([cell isKindOfClass:[TDBadgedCell class]])
{
NSLog(#"It is TDBadgedCell");
}
else
NSLog(#"It is NOT TDBadgedCell");
I had a similar issue in that I am subclassing UITableViewCell but not using storyboard. Here is my solution to using different cell classes dependent on if the user had purchase the unlock feature of the app. Hope it helps someone.
In a nutshell, I had the cell with several objects including a UITextView object. I wanted to lock down the copy and paste feature of the UITextView object in the lite version but then release the feature once the user had purchased in in-app product.
I had two UITableViewCell classes, one with UITextView as it is and another with UITextView subclassed with canBecomeFirstresponder returning NO. That way the user could still scroll up and down the UITextview data but not copy and paste the data.
Here is the code and all I had to do was rename the reuse identifiers.
WHY? Because [self.tableview reloadData] would not rebuild the cells with the new class as the cell was still in existence. New cells off the screen would get the new class but existing ones would not. This solution rebuilds all cells once off after the purchase unlocking the added feature.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (your test if in-app was purchased is yes)
{
static NSString *MyIdentifier = #"MyCell";
FrontCell *cell = (FrontCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[FrontCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.shouldIndentWhileEditing = NO;
}
//....///
cell.trackDetails.text = [yourObject objectAtIndex:indexPath.row];
cell.trackDetails.delegate = self;
cell.trackDetails.tag = indexPath.row;
return cell;
}
else // inapp not purchased
{
static NSString *MyLockedIdentifier = #"MyLockedCell";
FrontCellLocked *cell = (FrontCellLocked *)[tableView dequeueReusableCellWithIdentifier:MyLockedIdentifier];
if (cell == nil)
{
cell = [[FrontCellLocked alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyLockedIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.shouldIndentWhileEditing = NO;
}
//....///
cell.trackDetails.text = [yourObject objectAtIndex:indexPath.row];
cell.trackDetails.delegate = self;
cell.trackDetails.tag = indexPath.row;
return cell; }
}
In storyboard,you can set the Custom Class property for a subclass of UITablviewCell.
Then dequeueReusableCellWithIdentifier method will return cell with the type of your subclass.
I think you are using wrong method for dequeuing the cells.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [self.tblProfileInfo dequeueReusableCellWithIdentifier:#"PostCell" forIndexPath:indexPath];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
You forgot for indexPath at the end.
In iOS5, using ARC and prototype cells for tableView on storyboard, can I replace the code below:
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
return cell;
With this simple code??:
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:#"Cell"];
return cell;
I saw this on this link:
http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1
Thank's in advance!
Arildo
Sure, your code are right, storyboard automaticaly alloc new cells, this code work great:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RoadbookCell *cell = (RoadbookCell *)[tableView dequeueReusableCellWithIdentifier:#"RoadbookCell"];
//Configure cell
//[cell.lab1 setText:#"Test"];
return cell;
}
This is the way Apple intends it to be used, but I recommend against it. There is a bug that causes dequeueReusableCellWithIdentifier to return nil when VoiceAssist is enabled on a device. That means your app will crash for users with this option turned on. This is still a problem as of iOS 5.1.1
You can find more info and a workaround here:
http://hsoienterprises.com/2012/02/05/uitableview-dequeuereusablecellwithidentifier-storyboard-and-voiceover-doesnt-work/
The last paragraph has the work-around
I create a custom table view cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITextField *editField=nil;
...
NSString *CellIdentifier = [NSString stringWithFormat:#"cell:%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
// Configure the cell...
switch (indexPath.row) {
case 0:
{
cell.textLabel.text=DEVNAME_TEXT_NDVC;
cell.textLabel.font=[UIFont boldSystemFontOfSize:LABEL_TEXTSIZE_NDVC];
editField=[[UITextField alloc] initWithFrame:CGRectMake(158, 9, cell.frame.size.width-183, cell.frame.size.height-15) ];
editField.tag=DEVNAME_TAG_NDVC;
...
[cell.contentView addSubview:editField ];
[editField release];
}
break;
The table has 5 lines only, and each of them is on the screen always.
Later, when I try to get access to the cell I always get 'nil'
The following code should place cursor to apropriate UITextField when user tap the cell, but it doesn't, since 'cell' is always =0.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = [NSString stringWithFormat:#"cell:%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UITextField *tf=nil;
[tableView deselectRowAtIndexPath: indexPath animated: YES];
[activeField resignFirstResponder]; // Last used UITextField
switch (indexPath.row) {
case 0: //
tf=(UITextField*)[cell.contentView viewWithTag:DEVNAME_TAG_NDVC];
[tf becomeFirstResponder]; // Show the keyboard
//[tf performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.7];
break;
Please, could you suggest what is wrong? Why [tableView dequeueReusableCellWithIdentifier:CellIdentifier] always =0,
but all of the table cells are always visible.
Thanks.
Maybe I don't understand the question, but don't table cells only become reusable once they are no longer being displayed? If they are still visible, how could you reuse them?
Change this:
NSString *CellIdentifier = [NSString stringWithFormat:#"cell:%d",indexPath.row];
to:
static NSString *CellIdentifier = #“XXXX”;
Yes, dequeueReusableCellWithIdentifier: always return nil EXCEPT using registerNib:forCellReuseIdentifier:.
I have been trying to figure out how to set the accessoryType to UITableViewCellAccessoryCheckmark when the cell is selected but am having trouble finding a decent example of this.
If you know how to do this or a good tutorial could you please let me know that would be great.
To restrict the user to just one selection, meaning to create an exclusive list of one choice only, you could follow these steps;
Firstly, have a global index path declared in your .h file to keep track of the already selected cell ->
NSIndexPath *oldIndexPath;
When you create the cells, be sure to set the accessory type to none, so that no cell is selected by default when the table is seen;
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CellIdentifier"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"CallIdentifier"];
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
Finally, in the didSelectRowAtIndexPath delegate method, add the following code which will remove the checkmark from the already selected cell, and add a checkmark to the newly selected one.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (oldIndexPath==nil) { // No selection made yet
oldIndexPath=indexPath;
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
else {
UITableViewCell *formerSelectedcell = [tableView cellForRowAtIndexPath:oldIndexPath]; // finding the already selected cell
[formerSelectedcell setAccessoryType:UITableViewCellAccessoryNone];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark]; // 'select' the new cell
oldIndexPath=indexPath;
}
}
Hope this works out! :)
Something like this may work:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:myTableView cellForRowAtIndexPath:indexPath];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}
To answer the comment below, just push a viewController in the same method like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:myTableView cellForRowAtIndexPath:indexPath];
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
// Then push a new view
iPhoneCustomViewController *myVC = [[iPhoneCustomViewController alloc] initWithNibName:#"iPhoneCustomViewController" bundle:nil];
[self.navigationController pushViewController:myVC animated:YES];
[myVC release];
// And deselect the row (if desired)
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Did you know that:
1.) UITableView keeps track of the index paths for the rows that have been selected? It's in an array called indexPathsForSelectedRows
2.) UITableView has a flag you can set to make it either single or multiple selection. You can change it by calling the setter setAllowsMultipleSelection:(BOOL).
So, assuming that the table has been set to single selection, we can do the following in the tableView:CellForRowAtIndexPath method ...
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *simpleTableIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
[[cell textLabel] setText:#"Some Text"];
NSArray *selectedIndexPaths = [tableView indexPathsForSelectedRows];
if ([selectedIndexPaths containsObject:indexPath]) {
[cell setAccessoryType:UITableViewCellAccessoryCheckmark];
}else{
[cell setAccessoryType:UITableViewCellAccessoryNone];
}
return cell;}
This implementation of CellForRowAtIndexPath will give you a clean checkmark with no gray background when a cell is selected. You will need to set the checkmark in the didSelectRowAtIndexPath delegate method to make sure a cell gets the checkmark the moment it gets selected.
No need to create separate ivars or anything else to keep track of what was or wasn't selected. It's all neatly contained in the UITableView as Apple intended.
UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
newCell.accessoryType=UITableViewCellAccessoryCheckmark;
Implement this in didSelectRowAtIndexPath delegate method
From the docs:
The delegate handles selections in this method. One of the things it
can do is exclusively assign the check-mark image
(UITableViewCellAccessoryCheckmark) to one row in a section
(radio-list style). This method isn’t called when the editing property
of the table is set to YES (that is, the table view is in editing
mode). See "Managing Selections" in Table View Programming Guide for
iOS for further information (and code examples) related to this
method.
Here is an example:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]
cell.UITableViewAccessoryType = UITableViewCellAccessoryCheckmark;
}