Im working in a simple app that shows products in a tableview, i get the data from a json api. the problem is the table get laggy because of synchronous download of the images. im trying to use SDWebimage framework to solve this but i get this error:
"'NSInvalidArgumentException', reason: '-[UIImageView sd_setImageWithURL:placeholderImage:]: unrecognized selector sent to instance "
the code i have is this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
NSDictionary *dictionary = [self.json objectAtIndex:indexPath.row];
cell.textLabel.text=[dictionary objectForKey:#"name"];
cell.detailTextLabel.text = [dictionary objectForKey:#"description"];
//old synchronous code
/* NSString *path = [[[[dictionary objectForKey:#"images"] objectAtIndex:0] valueForKey:#"thumb"] valueForKey:#"url"];
NSString *ruta = [NSString stringWithFormat:#"http://18.221.78.126/ecoferia%#",path];
NSURL *url = [NSURL URLWithString:ruta];
NSData *imgData = [NSData dataWithContentsOfURL:url];
cell.imageView.image = [UIImage imageWithData:imgData];
*/
//trying to do asynchronous download of the images
NSString *path = [[[[dictionary objectForKey:#"images"] objectAtIndex:0] valueForKey:#"thumb"] valueForKey:#"url"];
NSString *ruta = [NSString stringWithFormat:#"http://18.221.78.126/ecoferia%#",path];
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:ruta]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
also i have the SDWebimage installed in my proyect and imported in the .m file like this:
#import "SDWebImage/UIImageView+WebCache.h"
im super new to objective-c so all the tips are appreciated.
REGARds..
The solution was put $(inherited) in Build Settings -> Other Linker flags.
Working good :)
Related
My problem is ---- I am using UITableView in custom cell when load data in tableview it is loading description and title also date but not load images in tableview.some times load all images in one row. When i am using this code scroll is working good but not load images in TableView. I want lazy load concepts. go to below link.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
newsobject=(newscustamCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (newsobject ==nil)
{
[[NSBundle mainBundle]loadNibNamed:#"newscustamCell" owner:self options:nil];
}
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(q, ^{
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"%#",[[newsarry objectAtIndex:indexPath.row] objectForKey:#"image_file"]]];
/* Fetch the image from the server... */
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
/* This is the main thread again, where we set the tableView's image to
be what we just fetched. */
newsobject.allnewsimg.image = img;
// newsobject.allnewsimg.image=[UIImage imageWithData:data];
});
});
newsobject.allnewsdes.text=[[[newsarry objectAtIndex:indexPath.row] objectForKey:#"long_desc"]stringByStrippingTags];
newsobject.allnewslabel.text=[[newsarry objectAtIndex:indexPath.row] objectForKey:#"title"];
![enter image description here][1]
newsobject.timelabel.text=[[newsarry objectAtIndex:indexPath.row] objectForKey:#"date"];
return newsobject;
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"%#",[[newsarry objectAtIndex:indexPath.row] objectForKey:#"image_file"]]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
[tableView cellForRowAtIndexPath:indexPath].imageView.image = image;
});
});
You can use some Lib like SDWebImage or Afnetworking. Libs support lazy load image and auto cache this image.
All you need to do:
[cell.imageView setImageWithURL:[NSURL URLWithString:#"url of image"]
placeholderImage:nil];
I will share you some code. Please follow this code.
First you have to retrieve all data from server. After that load on tableview.
Try this code :
- (void)viewDidLoad
{
NSString *urlString=#"http://put your url";
self.responseData=[NSMutableData data];
NSURLConnection *connection=[[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]] delegate:self];
NSLog(#"connection====%#",connection);
}
JSON delegate methods -------------
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[self.responseData setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.responseData appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
self.responseData=nil;
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSArray *response_Array = [NSJSONSerialization JSONObjectWithData:self.responseRankData options:kNilOptions error:nil];
NSDictionary *dataDictionary=nil;
NSDictionary *imgurlDic=nil;
NSArray *imgDic=nil;
// I am retrieve image according to my url. So please edit this according to your url. i am just write the format.
for (int i=0; i < response_Array.count; i++)
{
imgurlDic = [imgDic objectAtIndex:i];
imgDic = [dataDictionary objectForKey:#"imageKey"]; // Take your Key according to URL
NSURL *url = [NSURL URLWithString:[imgurlDic objectForKey:#"url"]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc] initWithData:data];
[self.Img_Array addObject:img]; // Adding all image in your image Array.
NSLog(#"imgurlDic == %#",imgurlDic);
}
[self.tableview reloadData]; // reload your tableview here.
}
Getting all image in your array you can use this array in your cellForRowAtIndexPath method or where you want to load the images.
Because of the way that table view cells are quickly queued/reused, by the time that your network request finishes loading the cell you are referring to might not be the same one you think it is (because it went offscreen and got queued and then reassigned to a different indexPath). Also, before returning the cell you need to make sure you reset its imageView’s image property to nil or some default value. See my answer here for more info: https://stackoverflow.com/a/23473911/91458
Also - it’s a good idea to keep a cache of the images you already retrieved. Check the NSCache class, which is very similar to using a mutable dictionary but with built in memory management, etc.
I also used that trick, but the threads and the indexes between threads make a mess so I searched and I found a great library on GitHub.
SDWebImage
You just need to #import <SDWebImage/UIImageView+WebCache.h> to your project, and you can define also the placeholder when image is being downloaded with just this code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier;
CustomCell *cell = [tableview dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
MyObject *object = (MyObject *)[self.list.array objectAtIndex:indexPath.row];
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
It also cache downloaded images and gives you great performance.
Hope it will help you!
I hope this link help for you. by using asyn classes image loading async...
https://github.com/nicklockwood/AsyncImageView
I have a UITableview that has a list of images like so. But it is very laggy for some reason when I scroll up and down. Any way to stop this? The images dont need to be reloaded. It needs to stay static.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
ContentModel *contentModel = [self.tableArray objectAtIndex:indexPath.row];
NSURL *url = [NSURL URLWithString:contentModel.txtImages];
NSData *data = [NSData dataWithContentsOfURL:url];
cell.imageView.image = [[UIImage alloc] initWithData:data];
return cell;
}
You may better use AFNetworking UIImageView Methods:
[imageView setImageWithURL:[NSURL URLWithString:#"http://image.com/image.png"]];
Even with a PlaceHolder:
[self.image setImageWithURL:[NSURL URLWithString:#"http://image.com/image.png"] placeholderImage: [UIImage imageNamed:#"logo"]];
https://github.com/AFNetworking/AFNetworking
This will dramatically reduce the lag on your tableview.
We have very similar requirements but my code works perfectly, I am guessing that
these commands slow down your routine:
NSURL *url = [NSURL URLWithString:contentModel.txtImages];
NSData *data = [NSData dataWithContentsOfURL:url];
cell.imageView.image = [[UIImage alloc] initWithData:data];
instead I use the following:
NSString *icon = CurrentQRCode.parsed_icon;
NSString *filePath = [[NSBundle mainBundle] pathForResource:icon ofType:#"png"];
UIImage *image_file = [UIImage imageWithContentsOfFile:filePath];
cell.imageView.image = image_file;
the whole routine is below:
QRTypeCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[QRTypeCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];}
CurrentQRCode = (QRCode *)[fetchedResultsController objectAtIndexPath:indexPath];
NSString *icon = CurrentQRCode.parsed_icon;
NSString *string = CurrentQRCode.parsed_string;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
NSString *filePath = [[NSBundle mainBundle] pathForResource:icon ofType:#"png"];
UIImage *image_file = [UIImage imageWithContentsOfFile:filePath];
cell.imageView.image = image_file;
cell.labelview.text = string;
I believe I originally tried handling UIImages using initWithData but found them a lot slower than the other methods.
Never ever ever ever load data from a network on the main thread. Use GCD or a framework like AFNetworking or restkit. The worst thing you could ever do to you app is lock up your main thread with things that can be computed off the main thread. A good rule of thumb is to ask is this something that is updating the UI, if not think about moving it to another thread.
I would also look at some of the WWDC videos about multithreading and anything related to moving off the main thread if you really want to understand the reasons why.
AFNetworking
RestKit
GCD
I have a custom class that parses through an XML and gets URL strings for images (which I store in an array). Then I want to retrieve those strings to load images and display each in a UITableViewCell. Here is my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *labelText = (UILabel *)[cell viewWithTag:1000];
labelText.text = [[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:#"name"];
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
cell.imageView.image = image;
return cell;
}
Obviously, when I scroll down the table, the app loads more images, and when the images are loading, the user can't do anything. It looks like the app froze. The images I want to load are not very large (about 8kb). Maybe there is a way I could load and store them in the background? I also want to give more freedom to the user, like scrolling down and viewing cells with text but without an image. Then force the app to load the images for the cells the user is currently viewing.
Is there any way to modify my code or any other solution for proper loading of images from URL strings?
Any advice would be appreciated, thanks.
Try to load the images asynchronous, so the main thread doesn't block.
Your code would look as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *labelText = (UILabel *)[cell viewWithTag:1000];
labelText.text = [[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:#"name"];
NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = image;
});
});
return cell;
}
You can use the SDWebImage framework to load images asynchronously.
Please follow the link:
https://github.com/rs/SDWebImage
Just add the SDWebImage framework to your project and set the other linker flag '-ObjC', to use the framework.
Download link : https://github.com/rs/SDWebImage/releases
Use this for asynchronus way of loading imageview
- (UITableViewCell *)tableView:(UITableView *)_tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
UILabel *labelText = (UILabel *)[cell viewWithTag:1000];
labelText.text = [[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:#"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 *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:#"imageCell"]];
NSData *image = [[NSData alloc] initWithContentsOfURL:imageURL];
//this will set the image when loading is finished
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [UIImage imageWithData:image];
});
});
return cell;
}
Download this project and use asynimageveiw in your cell's imageview.. link
Use NSOperation and NSOperationQueue.A perfect solution for your problem.Use below link to see how it works.
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues
Or
Use AFNetworking library It also provide a category over UIImageview that download image asynchronously.
I have some project where i must paste image from JSON. I try to find any tutorial about this, try to see any video from this theme but nothing. So my problem have:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSDictionary *allDataDictionary = [NSJSONSerialization JSONObjectWithData:webdata options:0 error:nil];
NSDictionary *playlist =[allDataDictionary objectForKey:#"playlist_data"];
for (NSDictionary *diction in playlist) {
NSString *name = [diction objectForKey:#"text1"];
NSString *namesong = [diction objectForKey:#"text2"];
NSString *images = [diction objectForKey:#"image"];
NSLog(#"%#", images);
[array addObject:text1];
[array2 addObject:text2];
}
[[self tableTrack]reloadData];
}
I added text 1 to cell also text 2 its work perfect but how to add image to to array 3(image in cell in my tableView)?
I tried also to added for image but its not work for me:
NSURL *imageURL = [NSURL URLWithString:[appsdict objectForKey:#"image"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageLoad = [[UIImage alloc] initWithData:imageData];
cell.imageView.image = imageLoad;
Please help with my problem or maybe give some tutorial with parse image from JSON, also youtube haven't perfect tutorial from JSON parse. Thanks!
Don't keep multiple data source like array1, array2, array3 etc. It is not good coding and will confuse while debugging/fixing issues. Instead maintain single array with information for displaying in individual cell of the table view.
for (NSDictionary *dict in playlist) {
// array is single data source
[array addObject:diction];
}
Then while assigning data to the table view cell use,
- (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];
}
cell.text1 = [[array objectAtIndex:indexPath.row] objectForKey:#"text1"];
cell.text2 = [[array objectAtIndex:indexPath.row] objectForKey:#"text2"];
//For displaying image by lazy loading technique use SDWebImage.
[cell.imageView setImageWithURL:[NSURL URLWithString:[[array objectAtIndex:indexPath.row] objectForKey:#"image"]] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
// If no place holder image, use this way
// [cell.imageView setImageWithURL:[NSURL URLWithString:[[array objectAtIndex:indexPath.row] objectForKey:#"image"]] placeholderImage:nil];
}
Like I mentioned in comments for lazy loading images from the URLs in JSON response, use SDWebImage. Usage is simple like I have shown above. Alternately you can implement lazy loading yourself by studying this sample code from Apple: LazyTableImages
Hope that helps!
According to https://developer.apple.com/library/mac/documentation/Foundation/Reference/NSJSONSerialization_Class/Reference/Reference.html and http://www.json.org/ you can't keep binary data in JSON (unless it is in base64).
It seems there's a problem with your webdata which is not a valid JSON. Did you check what is set under #"image" key in that dictionary while debugging? You can use [NSJSONSerialization isValidJSONObject:webdata] as well to see if your data is ok.
//prepare your method here..
-(void)hitimageurl{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:replacephotostring]];
//set your image on main thread.
dispatch_async(dispatch_get_main_queue(), ^{
if (_photosArray==nil) {
_showImageView.image = [UIImage imageNamed:#"noimg.png"];
} else {
[_showImageView setImage:[UIImage imageWithData:data]];
}
});
});
For some reason, I can not get my base 64 encoded image to show in a UIImageView in a UICollectionView.
Here is my code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = #"task_cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSDictionary* json = [dataSource objectAtIndex:indexPath.row];
[cell setBackgroundColor:[UIColor whiteColor]];
UIImageView *image = (UIImageView *)[cell viewWithTag:100];
UILabel *text = (UILabel *)[cell viewWithTag:101];
NSString *ext = [[json objectForKey:#"image"] pathExtension];
NSString *final = [NSString stringWithFormat:#"data:image/%#;base64,%#", ext, [json objectForKey:#"b64img"]];
NSURL *url = [NSURL URLWithString:final];
NSData *imageData = [NSData dataWithContentsOfURL:url];
[image setImage:[UIImage imageWithData:imageData]];
text.text = [json objectForKey:#"title"];
return cell;
}
DEBUG LOG:
Printing description of final:
data:image/jpg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/......(goes on for a while)
url = nil
imageData is nil
I do see the UILabel changing.
So, what should I try.
This is likely an issue with how you construct the path to your image. This looks funky:
NSString *final = [NSString stringWithFormat:#"data:image/%#;base64,%#", ext, [json objectForKey:#"b64img"]];
Please post the NSLog value of final, url and whether or not imageData is nil at time of setting image.
I had to do the following to fix it..
Change the code for the url:
NSURL *url = [NSURL URLWithString:[NSString URLEncodeString:final]];
Add the function URLEncodeString to NSString. I used the code from NSURL URLWithString:myString returns Nil?. It works like a charm.
And presto...it works.