As i am new in iPad application development please help me in simple problem.
I want to display images in the tableview from the server. it is displaying correctly.
But when i scroll table upside and come back to again there it will again download image data from server.
Please help me.
EDIT:
tableView:cellForRowAtIndexPath:
static NSString *CellIdentifier = #"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleBlue];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 75)];
[imgView setImage:[UIImage imageNamed:#"strip_normal.png"]];
[imgView setHighlightedImage:[UIImage imageNamed:#"strip_hover.png"]];
[cell addSubview:imgView];
[imgView release];
UIImageView *imgView1 = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 64, 64)];
imgView1.tag = indexPath.row;
UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.abc.com/abc/%#.png", [[arrMyCourses objectAtIndex:indexPath.row] valueForKey:#"CourseNumber"]]]]];
[imgView1 setImage:img];
[cell addSubview:imgView1];
[imgView1 release];
return cell;
Thanks in advance.
Your problem is this line
UIImage *img = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.abc.com/abc/%#.png", [[arrMyCourses objectAtIndex:indexPath.row] valueForKey:#"CourseNumber"]]]]];
You are downloading the image every time the table view requests for a cell and believe me this happens quite often as table view doesn't construct the entire view in a single shot. It reuses the cells going off screen to present new visible cells. So as soon as a cell goes off screen and comes back on, the cellForRowAtIndexPath: is called again. So the image gets downloaded once more. You are also downloading the image synchronously which will block the UI as well.
To fix this, you should consider downloading them once at the beginning and save them locally in a temporary location and load them into the memory as necessary. Having all of them in the memory can be costly. Additionally, move your download to the background using performSelectorInBackground:withObject. You will have to send the UIKit updates back to the main thread or else you will experience crashes.
Related
Hi my problem is that when I scroll TableView the image will appear in a wrong cell, after a few seconds the correct image appears.
here is my code
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
[cell setOpaque:NO];
[cell setBackgroundColor: [UIColor clearColor]];
PlaceData *data = [tableData objectAtIndex:indexPath.row];
UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
UILabel *sciNameLabel = (UILabel *)[cell viewWithTag:200];
UIImageView *thumbnailImageView = (UIImageView *)[cell viewWithTag:300];
nameLabel.text = data.name;
sciNameLabel.text = data.scientific_name;
// get a dispatch queue
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// this will start the image loading in bg
dispatch_async(concurrentQueue, ^{
NSURL *urlToPicture = [NSURL URLWithString:[NSString stringWithFormat:#"%#", data.thumbnail]];
NSData *imgData = [NSData dataWithContentsOfURL:urlToPicture options:0 error:nil];
// This will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
thumbnailImageView.image = tmpImage;
//dispatch_release(concurrentQueue);
});
});
return cell;
}
please help me
You can try adding following code to your cellForRowAtIndexPath -
1) Assign an index value to your custom cell. For instance,
cell.tag = indexPath.row
2) On main thread, before assigning the image, check if the image belongs the corresponding cell by matching it with the tag.
dispatch_async(dispatch_get_main_queue(), ^{
if(cell.tag == indexPath.row) {
UIImage *tmpImage = [[UIImage alloc] initWithData:imgData];
thumbnailImageView.image = tmpImage;
}});
});
You are reusing old cells, which is a good thing. However, you are not initializing the image in the cell's image view, which is not such a good thing. What you're describing happens because an old cell, with an image that was already loaded for that cell, is used for the new cell. You are then loading that cell's image in the background (which, again, is good) but it takes a few moments for that image to fully load. In the meantime, the image that was already loaded on the old cell, is displayed (and that's the reason you're seeing a wrong image in that cell, for a few moments).
The solution? add either
thumbnailImageView.image = nil
or
thumbnailImageView.image = someDefaultImageWhileYourRealOneLoads
right before dispatch_queue_t concurrentQueue ....
That way, you won't see the old (irrelevant) image while the real one loads.
I hope this helps. Good luck.
As becauase your ImageView is being loaded in an async dispatch call which is NOT on the main thread and is being called in some other thread so there is a delay in fetching the data from the URL and then converting it to an UIImage. THis process takes a bit of time as you know but you are scrolling the tableview in a faster rate. And as you know cellForRowAtIndexPath reuses any cell that is out of the window so the cell that is being reused might NOT fetched the imagedata that it WAS TO RETRIEVE previously when it was in the Window. Thus it loads the wrong data and then again when async is fired for that specific cell the cell loads that image but there comes the delay.
To overcome this feature as Chronch pointed it out u can leave the imageview as nil OR you can use AFNetworking's own UIImageView catagory which has a superb class to load imageview images quite elegantly
I'll leave u a link to it AFNetworking
I would do all my data binding at - tableView:willDisplayCell:forRowAtIndexPath: only because at cellForRowAtIndexPath your cell hasn't been drawn yet.
Another solution you can use is AFNetworking like someone else mentioned before me.
Swift 3.0
DispatchQueue.main.async(execute: {() -> Void in
if cell.tag == indexPath.row {
var tmpImage = UIImage(data: imgData)
thumbnailImageView.image = tmpImage
}
})
cell.thumbnailimages.image=nil
cell.thumbnailimages.setImageWith(imageurl!)
I think these two lines solve your problem.
I have tableview and I want to show all images from one folder inside directory and I have path to that directory. I am loading image to cell like:
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
for (UIView * view in cell.contentView.subviews){
if ([view isKindOfClass:[UIButton class]]) {
[view removeFromSuperview];
}
}
cell.imageView.image = nil;
cell.accessoryType = UITableViewCellAccessoryNone;
NSData *imgData = [NSData dataWithContentsOfFile:absoluteImagePath];
UIImage *thumbNail = [[UIImage alloc] initWithData:imgData];
UIImageView *imageView = [[UIImageView alloc] initWithImage:thumbNail];
[cell insertSubview:imageView atIndex:0];
}
but when I scroll up and down I get image over image inside cell (old image is visible below new, when I scroll on for example first position I see image for index 0 below other image ).
Does anyone know what is a problem, I am new to this iOS programming ?
You should
subclass your cell,
create an outlet for the image, and
design the layout in storyboard.
The benefits:
No need to check for (cell==nil)
You will have to write less code
Cleaner solution without redundant view creation errors
When the cell is reused, [cell insertSubview:imageView atIndex:0]; adds an additional UIImageView to the cell. Suggestion is to create a custom UITableViewCell and in the prepareForReuse method, remove the UIImageView. An easy way to identify UIImageView to remove is to add a tag to it. E.g.,
imageView.tag = 1000;
Then you can find the image view with
UIImageView *imageView = [self.contentView viewWithTag:1000];
if (imageView) {
[imageView removeFromSuperview];
}
Declare the UIImageView where you are initializing the cell.
For example
if (cell==nil)
{
UIImageView *image = [[UIImageView alloc]initWithFrame:yourFrame];
[cell addSubView:image];
}
image.image = yourImage;
Alternatively, you can remove all subviews from the cell before adding new UIImageView as subview.
I have am downloading multiple images from a web server at the same time, and I the problem is they are getting mixed up with each other.
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:#"website.com"]];
if (data == nil)
return;
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:data];
});
});
I got this code from: Getting Image from URL Objective C.
How do i fix this?? also is there any way to speed up download speeds?
You can use "AsyncImageView" class files it will load image synchronously and it shows the activity indicator while image loading
AsyncImageView is the class file in which it will create connection for each call and when image data downloading completed it will return image for imageview. and if image is already in cache then just return image without creating connection.
You can download "AsyncImageView" class files from following link:- https://www.dropbox.com/s/peazwjvky9fsjd7/Archive.zip
in .m file import AsyncImageView Class
#import "AsyncImageView.h"
in your tableview cell at indexpath method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SimpleTableCell";
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(X, y, width, height)];
NSString *imageURL = [NSString stringWithFormat: #"www.xyz.image.png"];
AsyncImageView *async = [[AsyncImageView alloc]initWithFrame:CGRectMake(0, 0, width, height)];
[async loadImageFromURL:[NSURL URLWithString:imageURL]];
[imageView addSubview:async];
[cell addSubview:imageView];
return cell;
}
try this your problem will solve.
In my iPhone application I have a tableview with custom imageview and loading images from remote location using AsyncImageView class. It works nicely, but one issue is, if I scroll the table, cells will be dequeued and it again trying to get the images from server. So, the method for loading image from AsyncImageView class is calling again and again hence increases the memory allocation, and eventually the app crashes.
Here is my code:
- (UITableViewCell *) getCellContentView:(NSString *)cellIdentifier {
CGRect CellFrame = CGRectMake(0, 0, 300, 40);
CGRect userImageFrame = CGRectMake(5, 7, 36, 36);
UIImageView *userImage;
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
[cell setFrame:CellFrame];
userImage = [[UIImageView alloc]init];
userImage.frame = userImageFrame;
userImage.tag = 3;
[cell.contentView addSubview:userImage];
[userImage release];
return cell;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
cell = [self getCellContentView:CellIdentifier];
else
[[AsyncImageLoader sharedLoader] cancelLoadingImagesForTarget:cell.imageView];
UIImageView *userImage = (UIImageView *)[cell viewWithTag:3];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.selectionStyle = UITableViewCellSelectionStyleGray;
NSString *url = [[NSString alloc]initWithFormat:#"%#%#", CommonImageURL,[AllUsersProfileImageArray objectAtIndex:indexPath.row]];
NSURL *imageUrl = [NSURL URLWithString:[url stringByAppendingFormat:#"?%i", rand()]];
[url release];
userImage.image = [UIImage imageNamed:#"defaultPerson.png"];
userImage.imageURL = imageUrl;
return cell;
}
Is there any possible way to fix the issue? Please help.
The best solution will be caching the image that is already downloaded and displaying it from there.
You need to write code for that, or there are some libraries which provide such feature:
HJCache
SDWebImage
The popular AFNetworking library also includes a UIImageView category to load images from the web which is often overlooked. I found it to be quite efficient with respect to memory usage and easy to use.
http://afnetworking.github.com/AFNetworking/Categories/UIImageView+AFNetworking.html
I came across same trouble of memory leaks loading multiple images from server. My application needed fresh images response every time (functionality factor)
I was using NSURLConnection using asynch requests. I tried with
NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:0];
[NSURLCache setSharedURLCache:sharedCache]; // in DidFinishLoading & didFailWithError
receivedData=nil; // at didReceiveResponse
receivedData=[[NSMutableData alloc] init];
but nothing really helped , until I removed my cell
[cell removeFromSuperview]; in cellForRowAtIndexPath and initialized it again FOR every new NSURL request (additional if condition on cellForRowAtIndexPath but REALLY that payed off).
Probably the UIImageViews were never removed and new images n data were added constantly as fetched by responses. Removing old UITableViewCell for a new NSURLRequest worked in my case. Hope this helps someone like me lost in cleaning NSURLCache and still memory beefing up.
I posted a custom solution here
Download image asynchronously .
I think it works ok and requires very little code.
Hi iam developing an app where i will fetch images from web service and load the images in tableview. I loaded the images asynchronously. The problem is my app get crash while scrolling the tableview and in log it shows memory recieved warning.Also same images gets repeated in many rows.Also it takes more time to load. i used the below code.
- (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];
/* UILabel * cellLabel=[[UILabel alloc] initWithFrame:CGRectMake(0, 50, cell.frame.size.width-20, 45)];
cellLabel.textColor=[UIColor blackColor];
cellLabel.backgroundColor=[UIColor clearColor];
cellLabel.tag=2;
[cell.contentView addSubview:cellLabel];*/
cell.selectionStyle=UITableViewCellSelectionStyleNone;
cell.backgroundView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"list_iPhone.png"]];
UIImageView *imv = [[UIImageView alloc]initWithFrame:CGRectMake(10,18, 48, 48)];
imv.tag=4;
imv.image=[UIImage imageNamed:#"ImagePlaceholder.png"];
[cell.contentView addSubview:imv];
UIImageView *arrwimv = [[UIImageView alloc]initWithFrame:CGRectMake(260,35, 14, 17)];
arrwimv.image=[UIImage imageNamed:#"arrw_iPhone.png"];
[cell.contentView addSubview:arrwimv];
UILabel *descriptionLbl=[[UILabel alloc] initWithFrame:CGRectMake(100, 27, 450, 45)];
descriptionLbl.font=[UIFont CITY311_TitleFontWithSize:18];
descriptionLbl.tag=1;
descriptionLbl.textAlignment=UITextAlignmentLeft;
descriptionLbl.textColor=[UIColor blackColor];
descriptionLbl.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:descriptionLbl];
UILabel *descriptionLbl2=[[UILabel alloc] initWithFrame:CGRectMake(100, 5, 450, 45)];
descriptionLbl2.font=[UIFont CITY311_TitleFontWithSize:18];
descriptionLbl2.tag=2;
descriptionLbl2.textAlignment=UITextAlignmentLeft;
descriptionLbl2.textColor=[UIColor blackColor];
descriptionLbl2.backgroundColor=[UIColor clearColor];
[cell.contentView addSubview:descriptionLbl2];
}
UIImageView *imv2=(UIImageView *)[cell.contentView viewWithTag:4];
dispatch_async(mainQueue, ^(void) {
if(![[[issueArray objectAtIndex:indexPath.row]objectForKey:#"PhotoUrl"] isEqualToString:#""])
{
NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:[[issueArray objectAtIndex:indexPath.row]objectForKey:#"PhotoUrl"]]];
UIImage* image = [[UIImage alloc] initWithData:imageData];
imv2.image = image;
}
});
UILabel *lbl=(UILabel *)[cell.contentView viewWithTag:1];
lbl.text=[[issueArray objectAtIndex:indexPath.row] objectForKey:#"issueSubmittedDate"];
UILabel *lbl2=(UILabel *)[cell.contentView viewWithTag:2];
lbl2.text=[[issueArray objectAtIndex:indexPath.row] objectForKey:#"IssueName"];
return cell;
}
In view did load
mainQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
In .h file
dispatch_queue_t mainQueue;
Please help to load the images properly without any memory warning(Crash).Thanks in Advance.
you are reusing tableviewcells. When a cell moves off the screen, it will be set aside so that you can reuse the object. When you are doing a dequeueReusableCellWithIdentifier you can get a 'old' cell that already contains an image. If you dont' clear the data from that cell you will see the old data (image) until the new image is downloaded.
inside the if(cell==nil) you should only create the cell and set properties that will be the same for every row. Set and clear the data below that if.
The crashes probably happen because a cell can be moved out of the view and reused for an other row before the initial callback is ready. Try setting the identifier to a unique value. Something like:
NSString *CellIdentifier = [NSString stringWithFormat:#"Cell%d", indexPath.Row];
Only keep your code like that when the number of rows is low. Otherwise you could get memory problems.
Instead of trying to fix it yourself, you could try using something like https://github.com/rs/SDWebImage