Large Images Crashing UITableView app - ios

So I've been searching for an answer to this and still am unable to figure it out. I have an array that contains 15 images or so I'm trying to display using a subview in a UITableViewCell. Code below - everything I've read mentions using autorelease/release to fix the issue, but I simply get ARC errors when attempting to do that. Any help would be much appreciated.
- (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];
}
int countthis = indexPath.row;
NSString *image = imgarr[countthis];
UIImageView *iv = [[UIImageView alloc] initWithFrame:(CGRect){.size={320, tableView.rowHeight}}];
iv.image = [UIImage imageNamed:image];
cell.imageView.image = iv.image;
return cell;
}

Large files tend to cause problems, no matter what your domain. Specifically, Apple says:
You should avoid creating UIImage objects that are greater than 1024 x 1024 in size.
It looks like you're trying to resize the image, but UIImages are immutable. So your allocated UIImageView serves only to waste processor cycles.
If you're stuck with big images that need to be scaled down, consider scaling before you assign them to the cell. You might find these routines useful: The simplest way to resize an UIImage?
Re autorelease/release: those have been deprecated since ARC. Your code doesn't appear to be leaking memory. I wouldn't sweat it. But you should edit your question to include details about the crash.

Your code can be cleaned up to this, which may help slightly with performance. You don't need to convert the indexPath.row to an int, since it's already a NSInteger, which is an architecture dependent type (int for 32-bit, long for 64-bit). You also probably want to use self.imgarr since its probably a property in your class. The image changes are as Neal mentioned.
- (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];
}
NSString *image = self.imgarr[indexPath.row];
cell.imageView.image = [UIImage imageNamed:image];
return cell;
}
As for autorelease/release, you mentioned you're getting ARC errors using them, which indicates you're on the iOS 5 or higher SDK. They are no longer needed in your code.

You can try to resize images using CGImageSourceCreateThumbnailAtIndexfirst before displaying them in the tableview.
If you have the path to the image you want to resize, you can use this:
- (void)resizeImageAtPath:(NSString *)imagePath {
// Create the image source (from path)
CGImageSourceRef src = CGImageSourceCreateWithURL((__bridge CFURLRef) [NSURL fileURLWithPath:imagePath], NULL);
// To create image source from UIImage, use this
// NSData* pngData = UIImagePNGRepresentation(image);
// CGImageSourceRef src = CGImageSourceCreateWithData((CFDataRef)pngData, NULL);
// Create thumbnail options
CFDictionaryRef options = (__bridge CFDictionaryRef) #{
(id) kCGImageSourceCreateThumbnailWithTransform : #YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : #YES,
(id) kCGImageSourceThumbnailMaxPixelSize : #(640)
};
// Generate the thumbnail
CGImageRef thumbnail = CGImageSourceCreateThumbnailAtIndex(src, 0, options);
CFRelease(src);
// Write the thumbnail at path
CGImageWriteToFile(thumbnail, imagePath);
}
More details here.

Related

Images display on UITableViewCell

I am trying to display images in UITableViewCell, I'm using web service to get the image names. Here is what i'm getting in the response of web service.
"2514466130834f61a9g38d57h0e2cb-25-ProfilePic.PNG",
"26144482598634297156heb8c0dafg-26-ProfilePic.PNG",
"2714464392629h60253gd4ec8b1f7a-27-ProfilePic.PNG",
"2814449113316a7gh5db0e2139c84f-28-ProfilePic.PNG",
"2914454043856e3519ah0dbf27cg84-29-ProfilePic.PNG"
The above response is saved in the array format. These array objects are then append on a url, And I need to display images on UIImageView.
Please Help me.
Prepare your url like below.
// Consider this is your prefix url
NSString *urlPrefix = #"https://your_url_prefix/"; // like http://stackoverflow.com/
// Consider this is the array you parsed from the web service
NSArray *resourceArray = #[#"2514466130834f61a9g38d57h0e2cb-25-ProfilePic.PNG", #"26144482598634297156heb8c0dafg-26-ProfilePic.PNG", #"2714464392629h60253gd4ec8b1f7a-27-ProfilePic.PNG", #"2814449113316a7gh5db0e2139c84f-28-ProfilePic.PNG", #"2914454043856e3519ah0dbf27cg84-29-ProfilePic.PNG"];
Prepare the resource url in your tableView's data source method: cellForRowAtIndexPath
NSString *resourceURL = [urlPrefix stringByAppendingString:[resourceArray objectAtIndex:indexPath.row]];
and use SDWebImage library to load the image and set that in to your cell imageView like
[cell.imageView setImageWithURL:resourceURL];
Note: SDWebImage lib can be downloaded from SDWebImage Git
Add this library files in your project and import like below to use.
#import "UIImageView+WebCache.h"
First download Asynchronous Class Here
Then put your .m file
#import "AsyncImageView.h"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpletableidentifier=#"simpletableidentifier";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:simpletableidentifier];
if(cell == nil)
{
cell.backgroundColor=[UIColor clearColor];
cell = [[UITableViewCell alloc ]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpletableidentifier];
}
AsyncImageView *Galleryimg = [[AsyncImageView alloc]initWithFrame:CGRectMake(0,0,40,40)];
Galleryimg.contentMode = UIViewContentModeScaleAspectFill;
Galleryimg.clipsToBounds=YES;
Galleryimg.tag=i;
Galleryimg.imageURL=[NSURL URLWithString:[Yourstring stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
[scroll addSubview:Galleryimg];
return cell;
}

Setting image in custom uitableviewcell causes lag

I have a uitableview where I use a custom cell. However, when I scroll the table view there is some serious lag. It happens when I set the UIImaveView's image property with an image. I am accessing an image from the directory. But since file IO is slow I am using the dispatch_async to load the image into a UIImage object on a separate thread.
However there is still some lag. When I scroll up and down the rows without any images, the scrolling is very smooth. However when the row actually has an image, there is lag. the momentum scrolling will halt, then the app becomes unresponsive, then when the image finally loads the momentum continues where it left off.
I am not sure what is causing the lag. At first I thought it had to do with the image being too large so i tried scaling it down. Still lags. Again, if I don't set the image in the custom cell there is no lag. But when I do set it there is lag. I am not sure how to fix this.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
DHTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kReuseIdentifierGoalCell forIndexPath:indexPath];
[self configureCell:cell forIndexPath:indexPath isForOffscreenUse:NO];
return cell;
}
- (void)configureCell:(DHTableViewCell *)cell forIndexPath:(NSIndexPath *)indexPath isForOffscreenUse:(BOOL)offscreenUse {
if (cell == nil) {
return;
}
[cell setDelegate:self];
PATH_TO_FILE = SQLITE_QUERY_TO_GET_PATH; //some pseudo codes
__weak typeof(sSelf)wSelf = sSelf;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__strong typeof(wSelf)sSelf = wSelf;
UIImage *unscaled_image = [UIImage imageWithContentsOfFile:PATH_TO_FILE];
UIImage *image = [unscaled_image imageScaledToFitInSize:kCellUIImageSize];
__weak typeof(sSelf)wSelf = sSelf;
dispatch_async(dispatch_get_main_queue(), ^{
__strong typeof(wSelf)sSelf = wSelf;
DHTableViewCell *cell = (id)[sSelf.tableView cellForRowAtIndexPath:indexPath];
if (cell) {
[cell.imageStored setImage:image]; //Commenting this out relieves all lag
}
});
});
}
#Calimari328 I hate if to put this as an answer because it does not really answer your question but the truth is what you should really do is use a library to achieve this. for example SDWebImage
Example:
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
You have to rethink this, cells get reused, that means each time you scroll your app will try to download images again.What about performance? memory ? cache ? processing?
As you think more about it is a lot more complex then a simple async task. Fortunately there are opensource projects to achieve this. No need to reinvent the wheel.
Please note I am not advertising any library if you want to write your own code you can do this as well. You can also search on the web for easy ones to implement.

Async image loading from url inside a UITableView cell - wrong image while scrolling

I had an IPhone application in which i am loading images asynchronously from an array ,using this in cellforrowatindexpath,
` - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
sCell *cell = (sCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[sCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSMutableDictionary *dicttable=[sarray objectAtIndex:indexPath.section];
NSString *icon= [dicttable objectForKey:#"thumb_image"];
cell.image.image = [UIImage imageNamed:#"thumbnail-default.png"];
if(icon.length!=0)
{
if(![[ImageCache sharedImageCache] hasImageWithKey:icon])
{ cell.image.image = [UIImage imageNamed:#"thumbnail-default.png"];
NSArray *myArray = [NSArray arrayWithObjects:cell.image,icon,#"thumbnail-default.png",[NSNumber numberWithBool:NO],nil];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate performSelectorInBackground:#selector(updateImageViewInBackground:) withObject:myArray];
}
else
{
cell.image.image = [[ImageCache sharedImageCache] getImagefromCacheOrUrl:icon];
}
}`
and all working fine,Because i enabled paging i need to call the request to load the next set of values, like this
`- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section==[sarray count]-1)
{
[self performSelectorInBackground:#selector(loadingfeedcntents:) withObject:count];
}
}`
but here the problem is when i am scrolling the table view wrong images are assigned to the image views in the custom cell,Can anybody help me on this ?
This is most probably due the reuse of the cells. Probably what is happening is that you trigger an asynchronous request to download an image, while that is happening you scroll the table an that cell is reused so when the image finishes downloading that new cell gets the image instead of the one originally requested it.
I think you will have to find another way to do it or detach that request from the cell on prepareForReuse
Many people is recommending this class "SDWebImage" for that situation (although I haven't test it)
I have been using SDWebImage for last 2 years and have used in 5 apps, Really easy and I would say it is the best way to load images in background with caching.
Please give a try. You will love it.
A nice tutorial is here.

UITableViewCell with UIImage scaling performance

The UITableViewCell has UIImage to be displayed within it.
Images are obtained from the server, and stored in NSDictionary as NSData.
This image is resized for non-retina devices using UIImageGraphicsBeginImageContext as listed below.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID=#"Cell";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellID];
NSDictionary *myDictionary=[self.tableObject objectAtIndex:indexPath.row];
NSData *imgData=[myDictionary objectForKey:#"icon"];
UIImage *img=[UIImage imageWithData:imgData];
if(isRetina]){
cell.iconImageView.image=img;
}else{
CGRect imgRect=CGRectMake(0, 0, img.size.width/2.0, img.size.height/2.0);
UIGraphicsBeginImageContext(imgRect.size);
[img drawInRect:imgRect];
UIImage *newImg=UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.iconImageView.image=newImg;
}
}
Would it be better approach and less memory intensive or should store it in the disk, and then access the image from it and assign it to cell.iconImageView.image;
write it to disk when you download it.
that way, os can lazy read the data, cache it and you dont have the image data in memory all the time

Loading local images from documents directory to tableview using SDWebImage

I am using a tableview which loads images from the documents directory, creates a thumbnail and shows it in the tableview. However, I have a problem: it becomes slow and crashes as the pictures are large, taken using the camera.
I have explored several solution including GCD to do the work in a background thread but the result is the same thing. So, I thought to look into SDWebImage but I don't know if it will also work for local files, not web images in this case. Can someone advise me please? If not, how is this problem solved? Is there an API that can help to resolve this issue?
That question is not easy to answer as the Question is asked fairly broad but I will do my best.
First, I usually dispatch a Background Thread if I have expensive processing to do as to not block the Main Thread, which is fairly important.
I don't really know why you are not using the normal UIImageView for what you are doing but try to implement following :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"YourCell";
MyCellClass *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[MyCellClass alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
/*
Whatever Code you want
*/
NSArray* params =#[cell.myImageView, #"http://myfancyimages.com/image.png"];
[self performSelectorInBackground:#selector(loadEventImageWithParameters:) withObject:params];
return cell;
}
And now add the function :
- (void) loadEventImageWithParameters:(id) parameters {
NSArray* params = [[NSArray alloc] initWithArray:(NSArray*)parameters];
NSURL *url = [NSURL URLWithString:[params objectAtIndex:0]];
UIImage *image = [UIImage imageWithData: [NSData dataWithContentsOfURL:url]];
UIImageView* theImageView = (UIImageView*) [params objectAtIndex:0];
[theImageView setImage:image];
}
If you got a lot of Pictures to load you are well advised to queue your Processes so you don;t "steal" all resources with Grand Central Dispatch.
Have a read through this excellent post http://www.raywenderlich.com/4295/multithreading-and-grand-central-dispatch-on-ios-for-beginners-tutorial for further details.
Hope that helped

Resources