delaying in loading images in the AQGridView in Objective-C - objective-c

In my app, I have a root view which is a gridview. I am loading an images in the gridview cells.I am having problem with the loading images. As soon as I launch the app, there will be blank cells with out images after a second the images are loading into the cells. Is there any way to solve this issue. The below is my code.
UIImage *defaultImage = [UIImage imageNamed:#"addEmployee.png"];
if (!employee.imageName) {
return defaultImage;
}
if ([_imageCache objectForKey:employee.imageName]) {
return [_imageCache objectForKey:employee.imageName];
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {
UIImage *image = [UIImage imageWithContentsOfFile:[self imagePath:employee.imageName]];
dispatch_async(dispatch_get_main_queue(), ^ {
if (image) {
[_imageCache setObject:image forKey:employee.imageName];
cell.imageView.image = image;
} else {
[_imageCache setObject:defaultImage forKey:employee.imageName];
cell.imageView.image = defaultImage;
return;
}
});
});
return cell.imageView.image;
}

Loading images takes time, there is no way to make it instant. Your code seems sensible, you are loading the images off the main thread so it will not block the UI, and caching them once loaded. The only way to make it faster is to load less images or make the images smaller.

Related

App Crashes when too many images are in TableView

I have a table that I am populating with information stored in Runner Objects. Each Runner object has a property called fileName that corresponds to an image saved in the document directory.
If I start with an empty table, I can add runners and their photos just fine until I reach about 8 runners. When I try to add a photo for the 8th or so runner, the app crashes. It seems to be a memory issue, but I don't see how this can be when all I'm storing locally are references to images in the directory.
I pass a runner object to my set up cell method in my RunnerCell class and set up the cell like so:
if (currentRunner.fileName != nil) {
if ([manager fileExistsAtPath:fullPath]){
UIImage *originalImage = [[UIImage alloc] initWithContentsOfFile:fullPath];
if ([currentRunner.photoOrientation isEqual: #"portrait"]) {
CGImageRef imageRef = [originalImage CGImage];
UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationRight];
self.runnerImageView.image = rotatedImage;
} else {
self.runnerImageView.image = originalImage;
}
self.addPhotoLabel.hidden = true;
}
}
As per documentation states that you mustn't load image to a UITableViewCell on main thread. That is why your app is crashing.
Because when you load image on main thread it became too much laggy when you scroll because dequereusable cell reassign cell and again load image for upcoming indexpath's cell and until the image loads it block the main thread which cause laggy behaviour of tableview.
Please load your cached image in a background thread. use this code.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
if ([currentRunner.photoOrientation isEqual: #"portrait"]) {
CGImageRef imageRef = [originalImage CGImage];
UIImage *rotatedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:UIImageOrientationRight];
dispatch_sync(dispatch_get_main_queue(), ^(void) {
self.runnerImageView.image = rotatedImage;
});
} else {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
self.runnerImageView.image = originalImage;
});
}
});

Lazy Loading UICollectionView Images

There's a UIImageView for every cell of the UICollectionView and I need to load them so that as you scroll through the collection they show up properly and every image loads on its cell at its index path, once.
Every image is downloaded from Kinvey.
Code to download every image:
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"Cell" forIndexPath:indexPath];
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, cell.bounds.size.width, cell.bounds.size.width)];
[cell addSubview:imageView];
[KCSFileStore downloadData:#"id for item at index path"] completionBlock:^(NSArray *downloadedResources, NSError *error) {
if (error == nil) {
KCSFile* file = downloadedResources[0];
dispatch_async(dispatch_get_main_queue(), ^(void){
imageView.image = [UIImage imageWithData:file.data];
});
NSLog(#"Downloaded.");
} else {
NSLog(#"Error: %#", error.localizedDescription);
}
} progressBlock:nil];
That "dispatch_async(dispatch_get_main_queue(), ^(void)..." is something I tried to solve my problem downloading images asynchronously, but that didn't work.
Thanks in advance.
why you try to download the images.
If you want to perform lazy loading for images in collectionView using third party library. then SDWebimages
is the best third party library for that.this library not only perform the lazy loading but provide multiple other option also. just take a look.
Hope this will Help You and solve your problem.

UIScrollView loading images -makes scroller be slowly

I have a scroll view with many UIViews, and these views have UIImageViews-that i load images into, while scrolling.
So every page has 4 of these views,that are preloaded to scroller at start, and i load the next page images while scrolling.
I do that in another thread , and still, scroller not entirely scrolls fast, if you look good it has some tiny flicks .
here is the function that loads them :
-(void)loadMainImageToIndex:(int)index
{
NSDictionary *dic=[mainData objectAtIndex:index];
NSString *userImageUrl=[dic objectForKey:#"url"];
NSURL *userUrl=[NSURL URLWithString:userImageUrl];
[self downloadImageWithURL:userUrl completionBlock:^(BOOL succeeded, NSData *tdata)
{
if (succeeded)
{
//load back
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^
{
UIImage *image=[UIImage imageWithData:tdata scale:0.25];
if (image)
{
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[image drawAtPoint:CGPointZero];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
dispatch_async(dispatch_get_main_queue(), ^
{
UIImageView *view=[mainImagesArray objectAtIndex:index];
});
}
}];
The url is loaded Asynch.
Turned out that the views shadow cause this thing :
//self.layer.shadowOffset=CGSizeMake(6.0, 6.0);
//self.layer.shadowColor=[UIColor blackColor].CGColor;
//self.layer.shadowRadius = 3;
//self.layer.shadowOpacity = 0.7;
Removing this solves the problem.
self.layer.shouldRasterize = YES; //solve the problem,

setImage: forState: lagging the UI

I have a tableview which runs some code for fetching a UIImage from NSData in a background thread, then in the foreground it calls [button setImage:image forState:UIControlStateNormal];.
The problem is that it freezes the UI for a split second on that line, until it's complete. This means that the UI freezes while it sets it as the image. Because the user is scrolling, this is very noticeable and jittery. Is there any way to avoid my UI from freezing like this?
dispatch_queue_t cellSetupQueue = dispatch_queue_create("Setup", NULL);
dispatch_async(cellSetupQueue, ^{
UIImage *image = [self.mediaDelegate largeThumbnailForMediaAtIndex:indexPath.row];
dispatch_async(dispatch_get_main_queue(), ^{
[self.thumbnailButton setImage:image forState:UIControlStateNormal];
});
});
dispatch_release(cellSetupQueue);
I'm guessing your images are JPGs. If so then UIImage will defer decoding the actual image until the first time it actually needs those pixels. Check out the ImageIO framework for more precise control on this. Here's a snippet I use:
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); // data is your image file NSData
if (source)
{
NSDictionary *dict = #{ (__bridge NSString *)kCGImageSourceShouldCache : #(YES) };
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)dict);
if (cgImage)
{
CFRelease(source);
return cgImage; // you can keep this reference so you dont have to decode again later
}
}
The important part is the kCGImageSourceShouldCache option. You can run this code on the background thread and pass the decoded image to the UI thread.

Check whether +[UIImage imageNamed:] found an image

I have an long if statement to decide what image to show in a UIImageView. They are all .png files, and I use the code:
if (whatever) {
image = [UIImage imageNamed: #"imageName.png"];
}
Does anyone know of a way to check to see if the program can find the image? So that if the image does not exist in the program, it can display an error image or something?
+[UIImage imageNamed:]
will return nil if it couldn't find a corresponding image file. So just check for that:
UIImage *image = [UIImage imageNamed:#"foo.png"];
if (image == nil)
{
[self displayErrorMessage];
}
The shortest snippet would be
image = [UIImage imageNamed:#"imageName"] ? : [UIImage imageNamed:#"fallback_image"]
but do you really want such code?
Make another check:
if (whatever) {
image = [UIImage imageNamed: #"imageName.png"];
if(image == nil) {
image = [UIImage imageNamed: #"fallback_image"];
}
}
it still can be shorted, like
if(! (image = [UIImage imageNamed: #"imageName.png"]) ) {
...
}
but you're toying with readability here.
First, I'd suggest using a dictionary instead of a long if. You can key each image name by whatever. If whatever isn't currently an object, make an enum that will encapsulate that information and use NSNumbers to box the values of the enum.
Then, you can check for nil when you try to retrieve the image. imageNamed: uses nil to indicate failure, so:
if( !image ){
// No image found
}