app slowing down due to image load issue - ios

I am having problem with the loading of content in the app, I see that the data is fetched by the app but the images take lot of time to load, is there any possibility to load images afterwords. The code is below:
NSDictionary *dict=[discussionArray objectAtIndex:indexPath.row];
UIImageView *avatarimage = (UIImageView *)[cell viewWithTag:4];
NSString *photoStrn=[dict objectForKey:#"photo"];
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSString *u=[NSString stringWithFormat:#"http://%#",photoStrn];
NSURL *imageURL=[NSURL URLWithString:u];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
dispatch_sync(dispatch_get_main_queue(), ^{
UIImage *dpImage = [UIImage imageWithData:imageData];
if (dpImage==nil)
{
dpImage = [UIImage imageNamed:#"profileImage.png"];
}
avatarimage.image = dpImage;
});
If you want more details I will provide :)

You can use GCD for doing this:
dispatch_async(dispatch_get_global_queue(0,0), ^{
for (NSDictionary *dic in discussionArray)
{
NSString *photoStr=[dic objectForKey:#"photo"];
NSString * photoString=[NSString stringWithFormat:#"http://%#",photoStr];
UIImage *dpImage = [UIImage imageWithData: [NSData dataWithContentsOfURL:[NSURL URLWithString:photoString]]];
if (dpImage==nil)
{
dpImage = [UIImage imageNamed:#"profileImage.png"];
}
}
});

Get SDWebImage Here and add that in your project and include
UIImageView+WebCache.h
in class implementation file
UIImageView *imag=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 40, 40)];
[imag setImageWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#",[[jsonarray objectAtIndex:indexPath.row]valueForKey:#"imgurl"]]] placeholderImage:[UIImage imageNamed:#"Icon#2x.png"]];
[self.view addSubview:imag];
[imag release];
SDWebImage will be more useful for loading images from URL.
Hope this Helps !!!

James,
With this line,
[NSData dataWithContentsOfURL:[NSURL URLWithString:photoString]]]
you make a synchronous network call on the main thread. The current thread will hang untile the network call is complete.
The solution would be to do an asynchronous network call. The AFNetworking library provides a great category to load images asynchronously : UIImageView+AFNetworking.

Related

Got memory pressure notification (critical/non-critical) - iOS objective-c

Got memory pressure notification (critical/non-critical).
ā€œnā€ numbers of images loading from API in tableview. But the app is crashing with above mentioned error after few seconds.
The current code is,
[post_img sd_setImageWithURL:[NSURL URLWithString:[arr objectAtIndex:i]] placeholderImage:[UIImage imageNamed:#"placeHolder.jpg"] options:SDWebImageRefreshCached];
I tried remove cache,
[[SDImageCache sharedImageCache] removeImageForKey:[arr objectAtIndex:i] fromDisk:YES withCompletion:nil];
Also, I tried to fetch images using dispatchqueue,
dispatch_async(dispatch_get_global_queue(0,0), ^{
NSData * data = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: [arr objectAtIndex:i]]];
if ( data == nil )
return;
dispatch_async(dispatch_get_main_queue(), ^{
post_img.image = [UIImage imageWithData: data];
});
});
But still I getting the error.

How to load images into uicollectionview cell from url asynchronous

I have a method that I'm calling from cell in the viewForRowAtIndexPath method.
The method containts only this code
NSURL *imageURL = [NSURL URLWithString:urlString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^(void){
completionBLock(image);
});
Images are loading very slowly and sometimes not at all. Everytime the cell goes back into view the method gets called again.
This is in my cellForIndexPathRow
[Method getImage:brochureModel.brochureCoverUrl Oncompletion:^(UIImage *image) {
cell.imageView.image = image;
}];
How can I download images from a url and display it in a collection view reusable cells.
Note: I'm trying to not use any third party libraries for my app
My problem is with the caching of the images. I tried this with self.imageCache being a mutableDictionary.
if (self.imageCache[urlString]) {
UIImage *imageFromCache = self.imageCache[urlString];
onImageDidLoad(imageFromCache);
} else {
[API fetchImageFromUrl:urlString onDidLoad:^(UIImage *image) {
self.imageCache[urlString] = image;
onImageDidLoad(image);
}];
}

Multiple webservice Making UI unresponsive

The problem is that I am calling multiple webservices on my homepage and the webservice is returning me the images and text from the server. During this process the UI become fully unresponsive for say 1-2 minutes which is looking very bad as I cant do anything. I heard about dispatch and tried to implement it but I dont get any results.May be I am doing something wrong.
What I want now that I want to that I want to run this process in background So that a user can interact with the UI during the fetching operation from the server. I am implementing my code just tell me where to use dispatch.
-(void)WebserviceHomeSlider{
if([AppDelegate appDelegate].isCheckConnection){
//Internet connection not available. Please try again.
UIAlertView *alertView=[[UIAlertView alloc] initWithTitle:#"Internate error" message:#"Internet connection not available. Please try again." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alertView show];
return;
}else {
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:#"text/html"];
[manager GET:ServiceUrl#"fpMainBanner" parameters:nil success:^(AFHTTPRequestOperation *operation,id responseObject)
{
//NSLog(#"JSON: %#", responseObject);
arrSlider = [responseObject objectWithJSONSafeObjects];
[_slideshow setTransitionType:KASlideShowTransitionSlide];
_slideshow.gestureRecognizers = nil;
[_slideshow addGesture:KASlideShowGestureSwipe];
// [_slideshow addImagesFromResources:[self getImages]]; // Add
// [_slideshow addTextFromResources:[self getText]];
// [slideShow subtextWithImages:[self getsubText]];
[_slideshow addImagesFromResources:[self getImages] stringArray:[self getText] stringsubArray:[self getsubText]];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
}
}
Just tell me where to use dispatch or edit my code using dispatch if possible. I have gone through some examples but still my concept is not clear. Which dispatch method id best (DEFAULT or BACKGROUND). I will be very thankful to You.
This is the code you are looking for . Just tell me where to edit it using dispatch
-(NSArray *)getText{
NSMutableArray *textArr = [[NSMutableArray alloc] init];
for(int i=0; i<[arrSlider count];i++)
{
texxt=[[arrSlider objectAtIndex:i ]valueForKey:#"title" ];
[textArr addObject:[texxt uppercaseString]];
}
return textArr;
}
-(NSArray *)getsubText{
NSMutableArray *subtext = [[NSMutableArray alloc] init];
for(int i=0; i<[arrSlider count];i++)
{
subbtext=[[arrSlider objectAtIndex:i ]valueForKey:#"tagline_value" ];
if(i==8)
{
subbtext=#"MAKE YOURSELF STAND OUT GET YOUR FREE CARDS!";
}
NSLog(#"subtext is,,.,.,,.,%#.%#",#"k",subbtext);
[subtext addObject:[subbtext uppercaseString]];
}
return subtext;
}
-(NSArray *)getImages
{
NSMutableArray *mArr = [[NSMutableArray alloc] init];
for(int i=0; i<[arrSlider count];i++)
{
pathh=[[arrSlider objectAtIndex:i ]valueForKey:#"filepath" ];
NSString *newString = [pathh stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
NSURL *imageURL = [NSURL URLWithString:newString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *originalImage = [UIImage imageWithData:imageData];
CGSize destinationSize = CGSizeMake(320, 158);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// [whotshotimgview setImage:image];
[mArr addObject: img];
}
return mArr;
}
[_slideshow addImagesFromResources:[self getImages] stringArray:[self getText] stringsubArray:[self getsubText]];
The problem here is that you didn't showed implementation of addImagesFromResources method. You probably have to implement GCD in this method because i guess you are fetching images and setting on UI by this your main thread is getting blocked.
You are trying to access the UIView from thread other than the main which causes the UI unresponsiveness.
Please use this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_slideshow setTransitionType:KASlideShowTransitionSlide];
_slideshow.gestureRecognizers = nil;
[_slideshow addGesture:KASlideShowGestureSwipe];
// [_slideshow addImagesFromResources:[self getImages]]; // Add
// [_slideshow addTextFromResources:[self getText]];
// [slideShow subtextWithImages:[self getsubText]];
[_slideshow addImagesFromResources:[self getImages] stringArray:[self getText] stringsubArray:[self getsubText]];
});
What I think is that your UI hangs due to downloading of image.
So you should use SDWebImage library that can help you to cache image and also prevent the UI getting hang. Using SDWebImage you can show the loader for the time the image is not loaded and it will cache the image. So from next time no image will be downloaded and also the UI will not hang. The link for complete reference to SDWebImage is :https://github.com/rs/SDWebImage
This block blocking your main UI to perform tasks so update this block as follow,
for(int i=0; i<[arrSlider count];i++)
{
pathh=[[arrSlider objectAtIndex:i ]valueForKey:#"filepath" ];
NSString *newString = [pathh stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
NSURL *imageURL = [NSURL URLWithString:newString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *originalImage = [UIImage imageWithData:imageData];
CGSize destinationSize = CGSizeMake(320, 158);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// [whotshotimgview setImage:image];
[mArr addObject: img];
dispatch_async(dispatch_get_main_queue(), ^{
// If you want to refresh your UI simultaniously, you can write statement for that i.e
// yourImageView.image = img;
// Otherwise remove this queue
});
});
}
The main thread should never be used for long processing. Its there for the UI.
AFNetworking provides asynchronous functionality for downloading files. You should also be careful of thrashing the network layer with too many simulatanous downloads. I think 4 or 5 is the max the last time I tested.
So the flow of execution should be as follows.
The controller sets up the slideshow, sends the downloading of the images to a background thread(automatically handled by AFNetworking), passing a completion block to it. In this completion block you need to dispatch the work back to the main thread using GCD, dispatch_async to dispatch_get_get_main_queue
Keep your main thread's run loop available for user interaction.
It is because the images are loading in another thread and you are populating your slider right after getting the data.
It is better to implement this protocol method of KASlidershow to use the slideshow in a more memory efficient way.
(UIImage *)slideShow:(KASlideShow *)slideShow imageForPosition:(KASlideShowPosition)position
{
pathh=[[arrSlider objectAtIndex:i ]valueForKey:#"filepath" ];
NSString *newString = [pathh stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
dispatch_queue_t myQueue = dispatch_queue_create("My Queue",NULL);
dispatch_async(myQueue, ^{
NSURL *imageURL = [NSURL URLWithString:newString];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *originalImage = [UIImage imageWithData:imageData];
CGSize destinationSize = CGSizeMake(320, 158);
UIGraphicsBeginImageContext(destinationSize);
[originalImage drawInRect:CGRectMake(0,0,destinationSize.width,destinationSize.height)];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// [whotshotimgview setImage:image];
[mArr addObject: img];
return img;
});
}
i didn't try the code but i hope it will work with little changes in variable names. Good luck

Loading takes a while when i set UIImage to a NSData with a url.

NSData *imageUrl = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.content objectAtIndex:indexPath.row] valueForKey:#"imageUrl"] ]];
cell.thumbnailImageView.image=[UIImage imageWithData:imageUrl];
this is how i use imageUrl to load them into UIImage but it takes a while to load and the program seems like it crashed or entered to an infinite loop.
How can i make the content of UIImage with url but faster?
I would suggest you to use AsyncImageView a beautiful implementation by Nicklockwood -father of iCarousel.
Link
it is very simple to use.
#import "AsyncImageView.h"
and in all imageViews do this.
[imageView setImage:#"default.png"];
[imageView setImageURL:#"whateverurl/whateverimage.png"];
In your case it would be:
[cell.thumbnailImageView setImageURL:#"yourURL"];
It works like a charm, and my code is in production. But if you still want your code to work try this:
UIActivityIndicator *activity =[[UIActivityIndicator alloc] initWithStyle:UIActivityIndicatorWhite];
[activity setFrame:CGRectMake(0,0,30,30)];
[cell.contentView addSubview:activity];
cell.thumbnailImageView.image=[UIImage imageNamed:#"Default~cell~image.png"];
dispatch_queue_t dispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(dispatchQueue, ^(void)
{
[activity startAnimating];
[self loadImages];
dispatch_sync(dispatch_get_main_queue(), ^{
NSData *imageUrl = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.content objectAtIndex:indexPath.row] valueForKey:#"imageUrl"] ]];
cell.thumbnailImageView.image=[UIImage imageWithData:imageUrl]; [activity stopAnimating];
[activty setHidden:YES];
});
});
This happens because every time your cell goes off screen your image is released and if you scroll it back on screen you will have to download your image again. Simply cache your image when downloaded and check if exists use it if not download from internet. You can use third party library like SDWebImage GitHub link
Import SDWebImageView+WebCache.h and in your cellForRowAtIndexPath use the following
[cell.thumbnailImageView setImageWithURL:[NSURL URLWithString:[[self.content objectAtIndex:indexPath.row] valueForKey:#"imageUrl"] placeholder:[UIImage imageNamed:#"placeholder.png"]];
use sdwebimage and down load the library file from here
the few steps you do follow
add the sdwebimage in your project after that
in your .h file
#import "UIImageView+WebCache.h"
in your .m file
call the single line in your cellforRowAtIndexPath
[cell.thumbnailImageView setImageWithURL:[NSURL URLWithString:[[self.content objectAtIndex:indexPath.row] valueForKey:#"imageUrl"]]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Use an NSOperationQueue so that you can perform the loading of the data on a background thread.
After that you should set the image on the main queue.
// Assume queue is created
[queue addOperationWithBlock:^{
NSData *imageUrl = [NSData dataWithContentsOfURL:[NSURL URLWithString:[[self.content objectAtIndex:indexPath.row] valueForKey:#"imageUrl"] ]];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.thumbnailImageView.image=[UIImage imageWithData:imageUrl];
}];
}];
For more about NSOperationQueue see the docs about NSOperationQueue.
So try to load image asynchronous.
https://github.com/nicklockwood/AsyncImageView
Use gcd to populate the images,
NSURL *imageUrl = [NSURL URLWithString:[[self.content objectAtIndex:indexPath.row] valueForKey:#"imageUrl"] ]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
UIImage *image = nil;
image = [UIImage imageWithData: [NSData dataWithContentsOfUrl: imageUrl]];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.thumbnailImageView.image = image;
});
})
You will probably want to store the image to some model object, also track if the download has been initiated, since you would not want to queue up the request for the same image url.

Response Lag in UITableView displaying JSON data

In my application I am parsing JSON data and then displaying that data in UITableView. The information is displayed in the table but the touch response is lagging really bad. I did some research and found out that it is recommended to implement asynchronous loading for the information and especially the image, but I could not find any relevant solutio that worked with my JSON application. I would appreciate some suggestions and comments of how to sort out this problem, here is the code:
jURL Definition www.website.com/info.json
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(jQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:
jURL];
[self performSelectorOnMainThread:#selector(fetchedData:)
withObject:data waitUntilDone:NO];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* jsonDict = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
calRes = [jsonDict objectForKey:#"results"];
[self.tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
NSDictionary *calDict = [calRes objectAtIndex:indexPath.row];
NSURL *imageURL = [NSURL URLWithString:[calDict objectForKey:#"image"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageLoad = [[UIImage alloc] initWithData:imageData];
cell.textLabel.text = [calDict objectForKey:#"name"];
cell.detailTextLabel.text = [calDict objectForKey:#"description"];
cell.imageView.image = imageLoad;
return cell;
}
I like to use the AFNetworking library for easy asynchronous image loading. You will need to include the library
#import "AFNetworking.h"
Then use it in the cellForRowAtIndexPath
[cell.imageView setImageWithURL:[NSURL URLWithString:[calDict objectForKey:#"image"]]
placeholderImage:[UIImage imageNamed:#"placeholder"]];
You also need to supply a placeholder image. I use a blank JPG of the size you will want the final image will be.
You can use SDWebImage from this link : https://github.com/rs/SDWebImage
It is the simplest and fastest library for asynchronous image loading.
It also provides image caching.
You can just done the whole operations by simply calling this function:
[cell.imageView setImageWithURL:jURL
placeholderImage:[UIImage imageNamed:#"your place holder here"]];
Just enjoy it.
As a follow up, it will be good to cache the images locally using this very handy plugin by Jake Marsh: JMImageCache
This way, the images will not need to be loaded from the URL the next time you boot the app.
See, the lagging in cellforRowAtIndexPath is because of this
NSURL *imageURL = [NSURL URLWithString:[calDict objectForKey:#"image"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageLoad = [[UIImage alloc] initWithData:imageData];
why don't you, use GCD for adding the image? Further more you can have your own NSCache to store the image so that every time the table reloads, the image can be directly loaded from the memory instead of firing the url.

Resources