iPhone sdk UITableView cell reuse identifier - iphone

I am using UITableView. When a book is downloaded i am adding a checkmark image to my table. And i have done this. But when am scrolling my table am getting the image even for non-downloaded books (i.e) my table cell are reused. For this i googled and checked my code with that. Everything seems to be the same. Here is my code,
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIImage *cellImage= [cacheImage getCachedImage:[listOfThumImages objectAtIndex:indexPath.row]];
if(cellImage == nil)
{
DeviantDownload *download;
download = [DownloadsArray objectAtIndex:indexPath.row];
cellImage = download.image;
if (cellImage == nil)
{
download.delegate = self;
}
NSLog(#"cellImage%#",cellImage);
UIImageView *imgView;
static NSString *CellIdentifier = #"";
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
imgView1 = [[UIImageView alloc] initWithFrame:CGRectMake(680, 60, 40, 40)];//self.view.bounds.size.width-
[imgView1 setImage:[UIImage imageNamed:#"Downloaded.png"]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"firstRun"])
{
if([[[appDelegate selectDB] valueForKey:#"bookname"] containsObject:[listOfBooks objectAtIndex:indexPath.row]] )
{
NSString *cellValue = [listOfBooks objectAtIndex:indexPath.row];
cell.contentView addSubview:imgView1];
}
return cell;
}
}
}
What's wrong with my code? Kindly help me out. Thanking You.

If you want that a cell does not display the image then simply remove the imageview, for that cell. set a view.tag for the image view, and retrieve it, then remove it.
What you are doing now, is to add a image sub view as soon as you need it for a cell, and later reuse that cell for all other rows. But you never removed the imageview for cells that dont need it.

CheckMarks with UITableView is little complicated then what it seems. First of all you'll have to save the status of your row which already has the bookmark. And, compare it in your cellForRowAtIndexPath method. What you can do is create on NSMutableArray. Save 1 for downloaded book and 0 for non-downloaded book. In your cellForRowAtIndexPath method compare [array objectAtIndex:indexpath.row] and set the image accordingly.
Also, change :
static NSString *CellIdentifier = #"";
To :
static NSString *CellIdentifier = #"MyCell";

Related

dequeueReusableCellWithIdentifier forIndexPath

I have a custom/sub-classed UITableViewCell using NSLayoutConstraints. The left most UIImageView may or may not be populated with an image, and if not, the objects to right of this field will naturally shift to the left. Works well within the custom UITableViewCell, however I'll be populating the content with my custom UITableViewController. Unfortunately, following code will populate every row cell.dispatchedView with an image -- though I've trapped to not populate rows with anything.
This has something to do with the dequeueReusableCellWithIdentifier. Ideas?
static NSString *CellIdentifier = #"CellIdentifier";
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView registerClass:[NXGActiveUnitsCell class] forCellReuseIdentifier:CellIdentifier];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath {
NSDictionary *item = [self.model.dataSource objectAtIndex:indexPath.row];
NXGActiveUnitsCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if([[[item objectForKey:#"cfs_no"] clean] length] > 0) {
cell.dispatchedView.image = [UIImage imageNamed:#"p.png"];
NSLog(#">>>%#",[item objectForKey:#"cfs_no"] );
} else {
NSLog(#">>> i got nothing so I should not touch this row...");
}
cell.departmentIconView.image = [UIImage imageNamed:#"f.png"];
cell.unitLabel.text = [item valueForKey:#"unit_id"];
return cell;
}
I don't know if this already solves your problem, but you should
definitely set
cell.dispatchedView.image = nil;
in the else-case, because cells are reused.

Custom UITableViewCell does not show the labels in storyboard

In this screen shot you can see that I have added UITableView in UIViewController then customized the UITableViewCell by adding some labels in it. But the issue is when I run the application. All of the cells are empty. There are no labels at all.
I am not getting what can be the issue. I have searched the web and read tutorials but couldn't resolve it.
I resolved this issue by myself, just after little effort.
Actually when you create a custom cell you have to do these things:
Setup the labels, images etc on storyboard cell.
Create a custom cell class (inheriting from UITableViewCell)(CustomCell.h & .m), CustomCell.h having all of the properties as iboutlet for labels, images etc. and synthesize them all in implementation.
After creating this custom class go back to storyboard, select the custom cell, change its class to the CustomCell and give it some identifier like "MyCustomCell", then right click on the custom cell and connect the IBOutlets with labels etc.
Now import CustomCell class in the class where you are implementing the UITableView and use the properties you defined in CustomCell class.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyCustomCell";
CustomCell*cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the provided setImageWithURL: method to load the web image
// Ensure you use a placeholder image otherwise cells will be initialized with no image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://example.com/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
cell.myCustomLabel.text = #"My Text";
return cell;
}
I did this all and my issue was resolved and please don't forget to connect the delegates & datasource and table.
Hope this will help others.
It is little bit late but you can solve your problem by setting the background color of your view as Clear Color from your storyboard.
in the tableview's delegate method , cellforrowatindexpath has a uitableviewcell inside it , there should be an identifier in the initialization of this cell. possibly it is "cell" or "cellIdentifier" .
you just need to select your cell from storyboard and enter this identifier string to the storyboard , where uitableviewcell's attribute inspector stays.
hope this helps.. :)
First Set cell identifier in storyboard #"Cell"
then set tag of label
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Create
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
UILabel *lblDate = (UILabel*)[cell viewWithTag:101];
UILabel *lblDistance = (UILabel*)[cell viewWithTag:102];
UILabel *lbltype = (UILabel*)[cell viewWithTag:103];
lblDate.text = [temp objectAtIndex:indexPath.row] valueForKey:#"date"] stringByReplacingOccurrencesOfString:#"-" withString:#"/"]];
lblDistance.text = [NSString stringWithFormat:#"%# KM",[[temp objectAtIndex:indexPath.row] valueForKey:#"distance"]];
NSString *type = [NSString stringWithFormat:#"%#",[[temp objectAtIndex:indexPath.row] valueForKey:#"distance"]];
if([type isEqualToString:#"0"])
{
lbltype.text = #"Personal";
}
else
{
lbltype.text = #"Bussiness";
}
// Configure
return cell;
}

UITableView not refreshed

I have an app consisting of a TabBar with a few TabBarControllers. One Controller contains a very simple table, which is supposed to display the contents of a NSMutableDictionary. When you hit the appropriate button, the Dictionary is updated in a separate Controller and the view switches to the UITableViewController, displaying the newly updated table.
I can see the Dictionary being updated. But the TableView never reflects the changes. In fact, it seems to display the changes only the 1st time I enter that screen.
I have tried [self table.reloadData] and while it gets called, the changes aren't reflected to the UITableView.
Does anyone have any suggestions? I am happy to post code, but am unsure what to post.
Update: the table is updated and refreshed properly only the 1st time it is displayed. Subsequent displays simply show the original contents.
Background:
The tableview gets filled from a dictionary: appDelegate.currentFave. The tableview should get refreshed each time the ViewController is invoked by the TabBarController.
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"in viewWillAppear");
[super viewWillAppear:animated];
[self loadFavesFile];
[self.tableView reloadData];
}
// load the Favorites file from disk
- (void) loadFavesFile
{
// get location of file
NSString *path = [self getFavesFilePath];
// The Favorites .plist data is different from the Affirmations in that it will never be stored in the bundle. Instead,
// if it exists, then use it. If not, no problem.
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
// read Faves file and store it for later use...
NSMutableDictionary *tempDict = [NSMutableDictionary dictionaryWithContentsOfFile:path];
appDelegate.sharedData.dictFaves = tempDict;
// grab the latest quote. Append it to the list of existing favorites
NSString *key = [NSString stringWithFormat:#"%d", appDelegate.sharedData.dictFaves.count + 1];
NSString *newFave = [NSString stringWithFormat:#"%#", appDelegate.currentFave];
[appDelegate.sharedData.dictFaves setObject:newFave forKey:key];
} else {
NSLog(#"Favorites file doesn't exist");
appDelegate.sharedData.dictFaves = nil;
}
}
// this gets invoked the very first call. Only once per running of the App.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// reuse or create the cell
static NSString *cellID = #"cellId";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
// allow longer lines to wrap
cell.textLabel.numberOfLines = 0; // Multiline
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.font = [UIFont fontWithName:#"Chalkduster" size:(16)];
cell.textLabel.textColor = [UIColor yellowColor];
// NOTE: for reasons unknown, I cannot set either the cell- or table- background color. So it must be done using the Label.
// set the text for the cell
NSString *row = [NSString stringWithFormat:#"%d", indexPath.row + 1];
cell.textLabel.text = [appDelegate.sharedData.dictFaves objectForKey:row];
return cell;
}
I found the problem. I was not properly initializing and assignng the TableView in my view controller. See below
- (void)viewDidLoad
{
[super viewDidLoad];
tableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.delegate = self;
tableView.backgroundColor=[UIColor blackColor];
self.view = tableView;
}
Assuming the code you have put up is correct, you want to use [self.table reloadData]. You have the . in the wrong place.
I had this same problem yesterday, for me it turned out I had set the wrong file owner in interface builder and hadn't set up the data source and delegates for the table view properly.
Try going into interface builder and right-clicking on the file owner, this should show you if anything isn't connected up properly.
You should make sure that your Interface Builder connections are set up properly, but what this problem really sounds like is that you have your UITableViewCell setup code in cellForRowAtIndexPath: inside your if(cell == nil) statement. Which it shouldn't be. Let me explain. If you have a list of cells, and you want to set the titles to each cell to a string in an array called myArray, right now your (incorrect) code looks like this:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellIdentifier"] autorelease];
[[cell textLabel] setText:[myArray objectAtIndex:[indexPath row]]];
}
return cell;
}
Can you see the problem with that logic? The cell will only get an updated title if no reusable cell can be found, which, in your case, sounds like the situation. Apple says that you should create a 'new' cell each time cellForRowAtIndexPath: is called, which means that you put all of your setup code outside of the if(cell == nil) check.
Continuing with this example, the proper code would look like this:
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier"];
if (cell == nil) {
// No cell to reuse => create a new one
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"cellIdentifier"] autorelease];
}
[[cell textLabel] setText:[myArray objectAtIndex:[indexPath row]]];
return cell;
}
This way, the cell gets assigned the proper string whether or not a reusable cell is found and so calling reloadData will have the desired effect.

UITableView repeating cells when scrolled

When i scroll down my UITableView, it starts showing me the same cells that i've already seen, and scrolling around a bit continues to put cells in the wrong place.
Here's the code i'm using. If anything additional is needed then let me know:
.h
#interface HomeViewController : UITableViewController {
int numberOfRows;
NSArray *allVaults;
}
#property (nonatomic, assign) int numberOfRows;
#property (nonatomic, retain) NSArray *allVaults;
#end
.m
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSFileManager *fileManager = [NSFileManager defaultManager];
self.allVaults = [fileManager contentsOfDirectoryAtPath:vaultsPath error:nil];
numberOfRows = [self.allVaults count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return numberOfRows;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.allVaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell.backgroundView = [AHCellCreation backgroundView];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.selectedBackgroundView = [AHCellCreation selectedBackgroundView];
cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
}
return cell;
}
Any help is appreciated!
EDIT 1: Image to show what happens when i move most code outside the (cell == nil) if statement:
Before:
After:
EDIT 2:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 82;
}
It looks as if you are only setting the cell content when the you're getting a nil back from dequeueReusableCellWithIdentifier. You need to set the cell contents every time, not just when you need to create a new cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
AHCell *cell = (AHCell*) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
// create a new cell if there isn't one available to recycle
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell = [AHCell blankCell];
}
// set the contents of the cell (whether it's a new one OR a recycled one)
NSString *vaultsPath = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/Vaults"];
NSString *dictionaryPath = [NSString stringWithFormat:#"%#/%#",
vaultsPath,
[self.allVaults objectAtIndex:indexPath.row]];
NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:dictionaryPath];
cell.backgroundView = [AHCellCreation backgroundView];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
cell.selectedBackgroundView = [AHCellCreation selectedBackgroundView];
// cell = [AHCellCreation createCellWithDictionary:dictionary Cell:cell];
[cell populateAHCellWithDictionary: dictionary];
return cell;
}
Update updated code to address second issue. Rework AHCell so that the class method, e.g. blankCell returns a new cell with the subviews set up and the instance method, e.g. populateAHCellWithDictionary: sets the content.
In that case AHCellCreation class must add the subviews to the cell and then set the text in one go? You need to layout the cell inside the if statement (add the subviews, UILabels, UIImageView etc, and set their frames etc). And set the content outside the if statement.
Basically whatever doesn't change in each row put inside the if statement, but what changes from row to row put outside the if statement.
This is because the code inside the if statement is only reached when the cell is created, almost always its the cells that are visible on the screen when the Table view loads are created.
When you scroll down the cells that disappear off the top of the screen are reused, and put at the bottom. This means that is you have 100 rows, it won't create 100 cells (it only creates the number of cells that can be visible on the screen at a time and the reuses those) , as this would consume a lot of memory, and the scrolling wouldn't be as smooth.

iphone - UITableView scroll problem

I have created a TableView in my application with 5 sections in it.
Sections 1 - 4 only contain one row each for the minute and section 5 contains 5 rows.
Everything works fine until I scroll the TableView off the screen. In my first section and row (cell) I have the accessoryView set to a UILabel with some text in it.
Every other cell has the disclosure button as the accessoryType.
When I scroll the tableView the text I have in the first cell somehow appears in the the last cell!?
I have set up my data by adding NSStrings to array's and then adding them as dictionaries to an NSMutableArray.
And here is my cell set up:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// UISwitch *aUISwitch = [[[UISwitch alloc]initWithFrame:CGRectZero]autorelease];
// Configure the cell.
NSDictionary *dictionary = [listOfSettings objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"Settings"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
if([cellValue isEqualToString:#"Status"]){
UILabel *viewLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 100, 20)];
[viewLabel setText:#"Connected"];
cell.accessoryView = viewLabel;
[viewLabel release];
}
else{
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
}
return cell;
}
I know cells get deleted/removed when they go off screen so I presume this has something to do with it? What is the recommended practice for dealing with cells that go off screen and reappear?
just at quick glance... in the else statement, not only do you need to set the cell.accessoryType, but also set the cell.accessoryView=nil;
the accesoryView is still there as the cell was recycled.