Images change while scrolling the table in iphone? - iphone

I'm using the xml for fetching the data and display it in the uitable. In the XML i have a separate tag for the image as "link". I'm displaying this image as the cell image. Everything workings fine when the applications loads, but when i scroll the table the images in the cell are getting changed.
- (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];
cell.accessoryType=UITableViewCellAccessoryNone;
cell.selectionStyle=UITableViewCellSelectionStyleNone;
//cell. separatorStyle=UITableViewCellSeparatorStyleNone;
titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(110, 10, 190, 130)];
[titleLabel setTag:2];
titleLabel.numberOfLines=7;
[titleLabel setFont:[UIFont systemFontOfSize:14]];
[titleLabel setTextColor:[UIColor blackColor]];
[titleLabel setBackgroundColor:[UIColor clearColor]];
[cell.contentView addSubview:titleLabel];
[titleLabel release];
imageView= [[UIImageView alloc] initWithFrame:CGRectMake(5, 20, 90, 110)];
[cell.contentView addSubview:imageView];
}
NSDictionary *appRecord = [responseArr objectAtIndex:indexPath.row];
NSString *urlLink;
urlLink=[NSString stringWithFormat:#"%#",[appRecord objectForKey:#"link"]];
NSLog(#"LINK : %#",urlLink);
if([urlLink length] ==0)
{
imageView.image=[UIImage imageNamed:#"ioffer.png"];
}
else {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlLink]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
imageView.image = image;
}
UILabel *desclbl = (UILabel*)[cell.contentView viewWithTag:2];
desclbl.text = [NSString stringWithFormat:#"%#",[appRecord objectForKey:#"description"]];
return cell;
}
I have used these code in the table's cellForRowAtIndexPath. responseArr is the array where i stored the links.
Can any one help me in this issue.
Thanks in advance
Malathi

If you reuse an existing table view cell, i.e. the code skips the first if-block, then imageView isn't initialized (where is it declared anyway?). That means even though you set a new text, some random image is reused.
The fix is to make sure that imageView is intialized in all cases.
And please do declare it locally in this method. Everything else seems very dangerous to me.

The "implied else" of the if (cell == nil) is that it's reusing a cell from the table's cache. It doesn't reset anything about those, it just picks them up and uses them. Which means that any element you've got on there needs to be cleared manually, especially if it's going to be some time before they're filled in.
You're already doing some defaulting, here:
if([urlLink length] ==0)
{
imageView.image=[UIImage imageNamed:#"ioffer.png"];
}
If you just make that NOT conditional -- ie just say
imageView.image=[UIImage imageNamed:#"ioffer.png"];
what you'll get is, your cell will load with its default image, then when the image loads remotely it'll replace it.
NOW: You're doing that image load synchronously. Which means your whole UI is going to freeze while the web request is being made. Not great.

I had the same issue. The cells are reused at an other index, when scrolling.
Try putting the code, that fills the cells with data, outside the "if (cell == nil)". Worked for me.
Best regards,
Alexander

You also use the custom cell instead of this and check each time
as like given below example-
Condition-
if(shout.creator.avatarImage){
avatarView.image = shout.creator.avatarImage;
}
else{
FetchImageOperation *operation = [[FetchImageOperation alloc] init];
operation.delegate = self;
operation.urlString = shout.creator.avatarURL;
[operationQueue addOperation:operation];
[operation release];
}
-(void)fetchthumbImageOperation:(FetchThumbImageOperation *)operation didFetchImage:(UIImage *)image
{
avatarView.image= image;
}
Here, i check the condition for image in tableview data source.
-(UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
}

Related

why tag changed in tableview to reuseing cells

I am creating custom cell contain UILabel , UIImageView ,using constant tag for UILabel , UIImageView using dynamic tag, the table have 11 cells, the first 7 cells loading correctly, the 8, 9, 10, 11 cell image view change when I am changing the 1, 2, 3, 4, cell respectively in the table, also the tags are same in the cells, I am using the images to check box in table,UITapGestureRecognizer used to change imageview in the table,
I am using this code.....
- (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] ;
cell.selectionStyle=UITableViewCellSelectionStyleGray;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
UIImageView *imageview=[[UIImageView alloc]initWithFrame:CGRectMake(5, 12, 20, 20)];
imageview.tag=n;
[cell.contentView addSubview:imageview];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tabimage:)];
imageview.userInteractionEnabled=YES;
[imageview addGestureRecognizer:tap];
imageview.image=[UIImage imageNamed:#"img1.jpeg"];
UILabel *titleLabel=[[UILabel alloc]initWithFrame:CGRectMake(30, 2, 260,26)];
titleLabel.tag=222;
titleLabel.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:titleLabel];
UILabel *dateLabel=[[UILabel alloc]initWithFrame:CGRectMake(30, 31, 260, 13)];
dateLabel.tag=333;
dateLabel.font=[UIFont systemFontOfSize:10];
dateLabel.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:dateLabel];
}
UIImageView *imageview1=(UIImageView*)[cell.contentView viewWithTag:n];
if([array containsObject:[NSNumber numberWithInt:imageview1.tag]]) {
imageview1.image=[UIImage imageNamed:#"img2.jpeg"];
} else {
imageview1.image=[UIImage imageNamed:#"img1.jpeg"];
}
UILabel *titlelable=(UILabel*)[cell.contentView viewWithTag:222];
titlelable.text=[task objectAtIndex:indexPath.section];
NSLog(#"%i",indexPath.section);
UILabel *dateLabel=(UILabel*)[cell.contentView viewWithTag:333];
dateLabel.text=[date objectAtIndex:indexPath.section];
n++;
return cell;
}
- (void)tabimage:(id)sender {
UIImageView *iv=(UIImageView *)[sender view];
int i=iv.tag;
NSLog(#"------------%i",i);
if (iv.image==[UIImage imageNamed:#"img1.jpeg"]) {
iv.image= [UIImage imageNamed:#"img2.jpeg"];
[array addObject:[NSNumber numberWithInt:i]];
} else {
iv.image= [UIImage imageNamed:#"img1.jpeg"];
[array removeObject:[NSNumber numberWithInt:i]];
}
}
You should change your cell identifier from static to dynamic that will solve your problem.
You should replace this
static NSString *CellIdentifier =#"Cell";
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:CellIdentifier];
with this
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:[NSString stringWithFormat:#"%d %d"], indexPath.row, indexPath.section];
Whatever problem you see, use of that "n" is most probably the problem. There is no good reason to use a shared variable to tag (and retrieve) views on table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath is called many times, it may be called several times for the same cell, and sometimes it finds a cell to reuse, and sometimes not.
I have of course no idea what n is, and how you use it elsewhere in your code, but you change it with every call here, and you cannot make assumptions when this method is called, so it's worthless.
You know for sure that every cell has one image with that tag, but if it will ever be accessible after the first run through this method or not is totally undefined, so with every reuse you basically get random behavior (either a fresh new cell or a reused one with a tag you don't know).
Ask yourself (or maybe at SO in another question): What are you trying to accomplish here?
i am getting solution for my app,
to using dynamic cell identifier in my code
NSString *CellIdentifier =[NSString stringWithFormat:#"%i-%i", indexPath.section,indexPath.row];
I suggest subclass the UITableViewCell add there all your custom elements, create setters for it. And in cellForRowAtIndexPath just call your setters.
As you are setting tag to imageview in if(cell == nil) block means tag will be set when cell is getting created. In your case 7 cells are getting created after that cells are reused by dequeuing. Hence for 8 onwards previously created cells will be used. This is why your tags are same as 1,2,3...for 8,9,10...cells.
Well, Alex suggested standard way to do it. But if you have not used custom cells yet, you can try this-
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier =#"Cell";
UITableViewCell *cell = [tableview dequeueReusableCellWithIdentifier:CellIdentifier];
UIImageView *imageview = nil;
if(cell){
for (UIView *view in cell.contentView.subviews){
if([view isKindOfClass:[UIImageView class]]){
imageview = (UIImageView*)view;
break;
}
}
}
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] ;
cell.selectionStyle=UITableViewCellSelectionStyleGray;
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
imageview=[[UIImageView alloc]initWithFrame:CGRectMake(5, 12, 20, 20)];
[cell.contentView addSubview:imageview];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tabimage:)];
imageview.userInteractionEnabled=YES;
[imageview addGestureRecognizer:tap];
imageview.image=[UIImage imageNamed:#"img1.jpeg"];
UILabel *titleLabel=[[UILabel alloc]initWithFrame:CGRectMake(30, 2, 260,26)];
titleLabel.tag=222;
titleLabel.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:titleLabel];
UILabel *dateLabel=[[UILabel alloc]initWithFrame:CGRectMake(30, 31, 260, 13)];
dateLabel.tag=333;
dateLabel.font=[UIFont systemFontOfSize:10];
dateLabel.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:dateLabel];
}
imageview.tag=n;
if([array containsObject:[NSNumber numberWithInt:imageview.tag]]) {
imageview.image=[UIImage imageNamed:#"img2.jpeg"];
} else {
imageview.image=[UIImage imageNamed:#"img1.jpeg"];
}
UILabel *titlelable=(UILabel*)[cell.contentView viewWithTag:222];
titlelable.text=[task objectAtIndex:indexPath.section];
NSLog(#"%i",indexPath.section);
UILabel *dateLabel=(UILabel*)[cell.contentView viewWithTag:333];
dateLabel.text=[date objectAtIndex:indexPath.section];
n++;
return cell;
}

iPhone: UITableView performance issue with imageWithContentsOfFile

I have performance issue in UITableView scrolling when I use imageWithContentsOfFile instead of imageNamed. Following is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
UniversalAppAppDelegate *delegate = (UniversalAppAppDelegate *) [[UIApplication sharedApplication] delegate];
NSDictionary *res = [[delegate comments] objectAtIndex:indexPath.section];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
......
// This line has a problem...when I use imageNamed here instead of imageWithContentsOfFile, it works absolutely fine
imageView = [[UIImageView alloc] initWithImage:[UIImage imageWithContentsOfFile:imagepath]];
[imageView setFrame: CGRectMake(30, 10, 55, 55)];
[sectionHeaderView addSubview:imageView];
......
cell.backgroundView = sectionHeaderView;
return cell;
I have also tried caching image but still same issue. Only imageNamed works perfectly fine but I can't use it as I need to specify path for image stored in documents folder and changes dynamically. Could anyone please tell me how could I resolve this issue?
Caching code:
NSMutableDictionary *thumbnailCache = [[NSMutableDictionary alloc] init];
- (UIImage*)thumbnailImage:(NSString*)fileName
{
UIImage *thumbnail = [thumbnailCache objectForKey:fileName];
if (nil == thumbnail)
{
NSString *thumbnailFile = [self filePath:fileName];
thumbnail = [UIImage imageWithContentsOfFile:thumbnailFile];
[thumbnailCache setObject:thumbnail forKey:fileName];
}
return thumbnail;
}
Usage:
imageView = [[UIImageView alloc] initWithImage:[delegate thumbnailImage:[res objectForKey:#"ImageName"]]];
You could try building an array of UIImages in the viewDidLoad method of the controller, and use the images in that array in your cellForRowAtIndexPath.
This will get rid of the need to call imageWithContentsOfFile for each image when drawing the tableview.
You could for example build the array in a seperate thread, and show a loading indicator while this happens. It would mean a small loading time at first, but a smooth scrolling tableview.

UITableView: How to offset text in cell?

Greetings,
I'm trying to implement a tableview with asynchronously loading images (80px x 60px). I have the async bit working, however my text (cell.textlabel.text) isn't showing up correctly - it overlaps the image!
Here is a photo of my iPhone:
Any ideas about how I might fix this?
I've tried setting a frame with CGRectMake, but it's not working... All out of ideas here and thought I'd ask the stackoverflow community.
Many thanks in advance,
Here is the code I am using:
-(UITableViewCell ) tableView:(UITableView)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath{
static NSString *CellIdentifier = #"ImageCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithFrame:CGRectZero reuseIdentifier:CellIdentifier]
autorelease];
} else {
AsyncImageView* oldImage = (AsyncImageView*)
[cell.contentView viewWithTag:999];
[oldImage removeFromSuperview];
}
CGRect frame;
frame.size.width=80; frame.size.height=60;
frame.origin.x=0; frame.origin.y=0;
AsyncImageView* asyncImage = [[[AsyncImageView alloc]
initWithFrame:frame] autorelease];
asyncImage.tag = 999;
NSString *urlStr = [[NSString alloc] initWithFormat:#"http://www.example.com/driverPhotos/%#/%#_80.png",[options objectAtIndex:[indexPath row]],[images objectAtIndex:[indexPath row]]];
NSURL* url=[NSURL URLWithString:urlStr];
[asyncImage loadImageFromURL:url];
[cell.contentView addSubview:asyncImage];
cell.textLabel.frame=CGRectMake(100, 0, 220, 60);
cell.textLabel.text=#"Hi";
return cell;
}
You can't change the frame's of any of the default subviews (detailTextLabel, textLabel, imageView, etc) If you really want to then you'll have to subclass UITableView and override Layout Subviews..
I'd suggest ignoring cell.textLabel and creating your own UILabel and adding it to cell.contentView in the same way you are adding your AsyncImageView (with tags, etc).
If you don't set anything on cell.textLabel then it won't appear and cover up your AsyncImageView. (however if it does then just bring you AsyncImageView to the front)
Btw, if you press the Home button and the lock button at the same time, the iPhone will take a screenshot for you and save it into the photoalbum. Where you can email it to yourself and get it on your PC.
Or you can have a custom cell and implement x, y in layoutSubviews:
-(void) layoutSubviews
{
[super layoutSubviews];
[[self textLabel] setFrame:CGRectMake(75,10,250,20)];
[[self detailTextLabel] setFrame:CGRectMake(75,30,250,20)];
}

UITableView is loading every cell

UITableview should only load cells that is visible at first right? My tableview is loading every cell initially which slows it down a lot. I'm using around 1000 rows. Only want it to load a cell when it has to (like user scrolling down). Anyone have any ideas why it's doing this?
I know cellForRowAtIndexPath is getting called initially for every cell. The height of the cells is 89.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UILabel *textName = nil;
UIImageView* image = nil;
unsigned int DATA_TAG = 1001;
unsigned int IMG_TAG = 1002;
// Retrieve a cell is Available
cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Check if no new cell was available
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
// Set the Accessory Data
textName = [[[UILabel alloc]initWithFrame:CGRectMake(cell.frame.origin.x, 80, cell.frame.size.width, 20)]autorelease];
textName.tag = DATA_TAG;
textName.textAlignment = UITextAlignmentCenter;
textName.backgroundColor = [UIColor clearColor];
textName.highlightedTextColor = [UIColor colorWithRed:1.0 green:1.0 blue:0.9 alpha:1.0];
textName.textColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
textName.lineBreakMode = UILineBreakModeWordWrap;
[cell.contentView addSubview:textName];
//Set the Image Data
image = [[[UIImageView alloc]initWithFrame:CGRectMake(cell.frame.origin.x, cell.frame.origin.y, cell.frame.size.width, 80)]autorelease];
image.tag = IMG_TAG;
image.contentMode= UIViewContentModeScaleAspectFit;
[cell.contentView addSubview:image];
}
Accessory* acc= [[AccessoryManager sharedManager].currentList objectAtIndex:indexPath.row];
if(acc == nil)
return cell;
textName= (UILabel*)[cell.contentView viewWithTag:DATA_TAG];
textName.text= acc.accessoryName;
image= (UIImageView*)[cell.contentView viewWithTag:IMG_TAG];
[image setImage:acc.accessoryImage];
return cell;
}
Can you please post some code? The method -tableView:cellForRowAtIndexPath is only called when a new "slot" for a potential cell opens up, so you'd need to be doing something majorly wrong to be "loading every cell" initially!
Do you perhaps mean that you're loading all the data initially, and wish to do that in batches?
That's right - it should only load the ones it has to. What is your UITableView's rowHeight? If that was extremely low, the Table might expect to have to load all the cells
If that's not the problem, can you paste in your tableView:cellForRowAtIndexPath: code?
Are you invoking cellForRowAtIndexPath yourself, for example from heightForRowAtIndexPath? If so, you don't need to create the cell to determine its height.

UITableView slow, even with just a few objects?

- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView setRowHeight:100];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
[self.view setBackgroundColor:[UIColor groupTableViewBackgroundColor]];
}
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 3;
}
// 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] initWithFrame:CGRectMake(0, 0, 320, 100) reuseIdentifier:CellIdentifier] autorelease];
// For Name/Phone:
UITextField *name = [[[UITextField alloc] initWithFrame:CGRectMake(75, 22, 200, 25)] autorelease];
[name setFont:[UIFont systemFontOfSize:14]];
[name setPlaceholder:#"John Doe"];
[name setReturnKeyType:UIReturnKeyDone];
[name setAutocapitalizationType:UITextAutocapitalizationTypeWords];
[name setAutocorrectionType:UITextAutocorrectionTypeNo];
[name setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
UITextField *phone = [[[UITextField alloc] initWithFrame:CGRectMake(75, 67, 200, 25)] autorelease];
[phone setFont:[UIFont systemFontOfSize:14]];
[phone setPlaceholder:#"0412 123 123"];
[phone setReturnKeyType:UIReturnKeyDone];
//[phone setKeyboardType:UIKeyboardTypePhonePad];
[phone setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter];
UIImageView *background = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 11, 302, 89)] autorelease];
background.image = [UIImage imageNamed:#"book-personaldetailsbg.png"];
// Add to the View
[cell addSubview:background];
[cell addSubview:name];
[cell addSubview:phone];
// Add actions:
[name addTarget:self action:#selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit];
[phone addTarget:self action:#selector(textFieldDone:) forControlEvents:UIControlEventEditingDidEndOnExit];
}
return cell;
}
Is there a reason for this? I only have a few objects set up so I can hardly see why it would be lagging. It jumps, and its not my phone because the settings app works fine.
UITableViewCells with many subviews are slow to render on the iPhone because of the way the phone renders each layer.
Read this: http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
The information in the above post is still hugely relevant and helpful, but the example project download link is broken (as of Dec 8th 2011). However, Apple's own table view documentation has had examples of flat table cells for fast scrolling for a log while now. Check it out: http://developer.apple.com/library/ios/#samplecode/AdvancedTableViewCells/Introduction/Intro.html
The main points are:
The iPhone doesn't handle Alpha very quickly
Subviews should have their opaque property set to YES
If possible, draw your subviews into a single opaque view for best performance.
Obviously, cells with controls that need to be manipulated can't be flattened, so I think you'll just have to try and get away with making sure your cell subviews are opaque.
The last thing I would suggest is to not allocate new objects every time a cell is requested - this is certainly a bottleneck in your code. You should use reusable cells, and subclass a cell so that its fields are allocated and added as subviews only the first time they are created. Subsequent calls should dequeue the cell and use prepareForReuse to clear its old values.
I thought you aren't supposed to set the cell height like you are. I believe you should implement the heightForCellAtIndexPath method (might not have that name 100% right) and it asks your delegate how tall it should make each individual cell. I feel that I've heard of performance issues if not done the "proper" way. IMHO
The first thing I'd try if I were you is to comment out all the image related code and see if the table speeds up drastically.
Start by removing that cell background. See if that makes a difference.
Here the solution guys, Inside the block add the images to load and that's it:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:
[[feeds objectAtIndex:indexPath.row]
objectForKey: #"url"]]];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imagen.image = image;
});
Use Grand Central Dispatch for priority.
I can explain if anybody want, just email me at info#tonymedrano.com