protect cell.textLabel.text on scroll event of UITableView - iphone

How can I protect Text of UITableViewCell, which gets changed on scrolling of the UITableView.
static NSString *CellIdentifier = #"Cell";
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(indexPath.row != [destinationList count])
{
if (cell == nil)
{
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.customLable.text = #"MyCustomLabel";
else
{
if (cell == nil)
{
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = #"Static Text to be set";
[cell.customLable removeFromSuperview];
}
Problem: Every time I scroll the UITableView, #"Static Text to be set" gets overwritten on #"MyCustomLabel".
How can I prevent this? I want all the cells of UITableView to retains their TextLabels through Table's LifeTime.

Two possible answers:
Generally it doesn't matter. Reusing the cell is how it's supposed to work and you should fully "reset" each cell each time. You shouldn't be storing state in the cell anyway
Create a new reuseIdentifier, one for the custom label and another for the static text

They will all retain their UILabels property, because cells get reused which is why you use:
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
However UITableView uses lazy loading meaning it ONLY keeps in memory visible UITableViewCells, therefore when you scroll the now invisible cells get reused, and now the new visible cells have the same UILabel as the ones that you can no longer see. That means there is really just a handful of UITableViewCells getting reused every time.
This is why in the docs UITableViewCell in the discussion for the instance method prepareforReuse:
If a UITableViewCell object is reusable—that is, it has a reuse identifier—this method is invoked just before the object is returned from the UITableView method dequeueReusableCellWithIdentifier:. For performance reasons, you should only reset attributes of the cell that are not related to content, for example, alpha, editing, and selection state. The table view's delegate in tableView:cellForRowAtIndexPath: should always reset all content when reusing a cell. If the cell object does not have an associated reuse identifier, this method is not called. If you override this method, you must be sure to invoke the superclass implementation.

You should make different identifiers for different cells.
Anyway, to store something in UITableView is a wrong approach, it just to display data, not to store.

Try this
static NSString *CellIdentifier = #"Cell";
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = nil;
cell.customLabel.text = nil;
if(indexPath.row != [destinationList count])
{
cell.customLable.text = #"MyCustomLabel";
}
else
{
cell.textLabel.text = #"Static Text to be set";
}

why do you remove the custom label? Just remove the text of the label.
if(indexPath.row != [destinationList count])
{
/* ... */
cell.textLabel.text = #"";
cell.customLable.text = #"MyCustomLabel";
else
{
/* ... */
cell.textLabel.text = #"Static Text to be set";
cell.customLable.text = #"";
}
and make sure that does labels are transparent.

Related

UITableView Reuse Causing Broken Cells

When scrolling my UITableView (tends to be when I scroll it fast) the data for my cells gets mixed up, so labels might be repeated etc.
I understand that reusing the cells probably causes this, but what if the user scrolls down the list really quickly and all the cells get mixed up, how am I supposed to avoid this?
Thanks.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"VideoListCell";
VideoListCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[VideoListCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
if (isPlaylistView)
{
//Fill cell with data
}
else if (isPlaylistDetailView || isSearchResultsView)
{
//Fill cell with data
}
else
{
//Playlist button and uploads
if (indexPath.section == 0)
{
//Fill cell with data
}
else
{
//Fill cell with data
}
}
return cell;
}
You generally use this kind of code:
cell = dequeReusableCell;
if (cell == nil) {
create cell;
initialize cell;
}
fill cell with actual data from current row
return cell;
If you will move code "fill cell with actual data from current row" into "if" — you will get the kind of behavior you get right now.
So the answer will be "fill cell with data after you initialize it, outside of "if (cell == nil)" block.
UITableView will ever only dequeue a cell for reuse if the position that the cell was in is currently off-screen. So you don't have to worry about "mix-ups".
static NSString *cellIdentifier=#"cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell==nil)
{
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil];
}
I think it will be helpful to you.
set dequeueReusableCellWithIdentifier to nil for example..
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
UPDATE:
See this Example... i load many data in the cell with also my custom Gridview...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* cellIdentifier = #"gridCell";
UITableViewCell *gridCell = [tableView dequeueReusableCellWithIdentifier:nil];
if(gridCell == nil)
{
gridCell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
}
return gridCell;
}
hope this help you....
In your custom cell class override prepareForReuse method. In this method set text of your labels to nil and set imageview's image to nil also. This function is called everytime when a cell is reused so your problem will be solved by this. May be like this
- (void)prepareForReuse{
[super prepareForReuse];
self.titleLabel.text = nil;
self.unitImageView.image = nil;
}

Prevent reloading of MKMapView inside UITableViewCell

I've made a subclass of UITableViewCell and added a MapView. Everytime I scroll the map of the screen and scroll back to that cell it loads again. (As you all know it's the default behaviour for cell reusing)
Is there anyway to prevent that? Or do you know any other tricks for that case? thanks a lot!
One possible solution:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
NSInteger row = [indexPath row];
UITableViewCell *cell = nil;
if( row != kMapCellRow ) { // defined value to whatever value it may be
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
}
else {
// use an ivar to track the map cell, should be set to nil in class init
cell = _mapCell;
}
if (cell == nil) {
if( row != kMapCellRow ) { // defined value to whatever value it may be
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
else {
// create your map cell here
cell = [[[MyTableViewMapCell alloc] init...
}
}
// configure it here
You could replace the defined kMapCellRow with an instance variable if it makes sense for your app.

UITableView not scrolling smoothly...(iPhone SDK) ..!

UITableView not scrolling smoothly...(iPhone SDK) ..!!
I have implemented UITableView DataSource and Delegate methods in an individual separate classes.(one for delegate and one for datasource) in main program i write only:
//assume that all objects are allocated
ObjTableView.dataSource=ObjDataSource;
ObjTableView.delegate = ObjDelegate;
[self.view addSubView: ObjTableView];
when i run this code , UITable view appears but when i try to scroll it, it doesn't scroll smoothly.
I have also checked that UITableViewCell doesn't redraw once the cell is initialized.
can any one tell me why this happens ? How can i solve this problem ??
From comments:
ListDataSource *ObjListDataSource = [[ListDataSource alloc]initWithArray:[[sender object] valueForKey:#"List"]];
ListDelegate *ObjListDelegate = [[ListDelegate alloc]initWithArray:[[sender object] valueForKey:#"List"]];
tblList = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 460)];
tblList.dataSource = ObjListDataSource; tblList.delegate = ObjListDelegate;
[self.view addSubview:tblList]; [tblShopList release];
More from comments:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = [NSString stringWithFormat:#"%i",indexPath.row];
UITableViewCell *cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,320,100) reuseIdentifier:CellIdentifier] autorelease];
//custom cell code
}
return cell;
}
More Information:
I have used NSNotification which notifies to current class when parsing is complete, after receiving notification , current class method calls DataSource, Delegate methods (which is defined in a separate class file).
So UItableViewCell customization (which is in ListDataSource) and table view(in current class) both are in different classes.
A problem is
NSString *CellIdentifier = [NSString stringWithFormat:#"%i",indexPath.row];
The id needs to be the same for all cells of the same class, otherwise you never reuse them. As you can see in most examples, it is indeed a constant in most (all?) cases.
Little explaination on the reuseIdentifier: every time a cell gets out of screen, you can reuse it instead of creating a new one. To reuse it, you need a cell in queue with the same identifier as the one you pass to dequeueReusableCellWithIdentifier. The way you did, the cells are never reused, because each id is unique (they may or may not be reused in case a row reappears on screen, depending on queue size, which is not configurable AFAIK). This is why personalization of the cell should happen OUTSIDE the "cell == nil" block. Long story short, you are using the reuseIdentifier not as intendend.
I think Michele is correct, but I would also add that it looks like you are doing your cell customization where the cell gets created. What you should be doing is something more like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = #"CellIdentifier";
UITableViewCell *cell = (UITableViewCell)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectMake(0,0,320,100) reuseIdentifier:CellIdentifier] autorelease];
//custom REUSABLE cell code here, e.g. text color, etc.
}
NSString *cellText = [dataArray objectAtIndex:indexPath.row]; //assuming you have a simple array for your data
cell.textLabel.text = cellText;
return cell;
}
I would also add that I'm not sure why you are able to run the app with the code you have here, since UITableViewCell cell = ... is an invalid initializer. It should be UITableViewCell *cell = ....
It would be helpful to see how you are customizing your cell, since without that it's hard to see what's happening.

no value entered in TableView row

I have a basic view with a UITextField and a Tableview. I am able to enter text to the TextView and capture the value through an IBOutlet and IBAction.
My IBAction is setMyNote within which I call the cellForRow... method.
In my cellForRow.. method I have this snippet code
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
NSString *enNote = [[NSString alloc] initWithString:enteredNote.text];
NSInteger nRow = [indexPath row];
switch (nRow) {
case 1:
cell.textLabel.text = enNote;
break;
default:
break;
}
[enNote release];
return cell;
}
When I run my debugger, I can see that enNote has the new text I entered on the textView and it seems to get assigned to my cell.textLabel.text too -- but my table does not show this new text. What am I missing?
You shouldn't call the cellForRow directly.
When the table view renders itself it calls the cellForRow method in order to create the cells that should be visible.
I think that you should update your table view data source instead of trying to update the cell directly and then reload the table view ([tableView reloadData];).

reloadData not working - suspect cell's reuseIdentifier

I have a view controller that gets presented modally and changes some data that effects the data in a uitableview in the modal's parent view controller (a table view).
I call the tableview's reloadData method when the parent view reappears. I have confirmed that this code gets hit with a break point. My trouble is, reloadData isn't working. Here's the kicker - if I don't use reuseIdentifiers in the - (UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath method, the data reloads correctly. It looks like the reuseIdentifier is to blame.
I really want to continue to use the reuseIdentifier for my cells - how do I make this work?
Got it figured, and you're right, it's not the reuseIdentifer.
I was nesting the content assignment right below the cell allocation like so:
// Dequeue or create a cell of the appropriate type.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = [targetRow.children count] ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
// Configure the cell.
cell.textLabel.text = [NSString stringWithFormat: targetRow.title];
}
Since the dequeued cell was found, the content wouldn't get updated.
I changed it to this and all works great now...
// Dequeue or create a cell of the appropriate type.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.accessoryType = [targetRow.children count] ? UITableViewCellAccessoryDisclosureIndicator : UITableViewCellAccessoryNone;
// Configure the cell.
cell.textLabel.text = [NSString stringWithFormat: targetRow.title];
The problem is unlikely to do with the reuse identifier. Please post your code for your tableView:cellForRowAtIndexPath: method.