The most common way to load UIImage from the web - iphone

Dear stackoverflowers,
I have gone astray.
In my app I load 2 images from the web, like this:
-(void)loadImages
{
...
image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:imgUrl1]];
image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:imgUrl2]];
}
In order to not blocking the main thread, I use GCD:
dispatch_async( dispatch_get_global_queue(0,0), ^{
[self loadImages];
After that, I use these images in my UITableView:
if (indexPath.row == 0)
{
cell.imageView.image = image1;
}
else
{
cell.imageView.image = image2;
}
Then I decided to add UIActivityIndicator, but faced some problems. I understand that my code is not correct. I saw that people using NSURLRequest and NSURLConnection to load images and add UIActivityIndicator.
Could you tell me please, what is the most standart way to load images like that ? What should I rewrite ?

I would suggest you to use AsyncImageView a beautiful implementation by Nicklockwood -father of iCarousel.
https://github.com/nicklockwood/AsyncImageView
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.imageView 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(100,100,30,30)];
[self.view addSubview:activity];
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(), ^{
[yourTableView reloadData];
[activity stopAnimating];
[activty setHidden:YES];
});
});

I can tell you what I usually do if I want to use my own custom code
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData * imageData = [NSData dataWithContentsOfURL:_pictureURL];
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = [UIImage imageWithData:imageData];
});
});
Otherwise you can consider the UIImageView+AFNetworking category that adds the method
setImageWithURLRequest:placeholderImage:success:failure:
to UIImageView.

There is nothing wrong with your code (assuming that you've confirmed that it works). Just because others use NSURLConnection and related doesn't make yours wrong.
To use the activity indicator do this:
-(void)loadImages
{
UIActivityIndicator *indicator = ...
...
[indicator startAnimating];
image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:imgUrl1]];
image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:imgUrl2]];
[indicator stopAnimating];
}
Small nit - personally I'd use:
cell.imageView.image = (0 == indexPath.row ? image1 : image2)
as nobody seems to appreciate the C 'conditional expressoin'. End small nit.

Related

Delay between UIActivityIndicatorView animation stop and content view refresh

I have created a simple UIActivityIndicatorView that takes care of informing the user about the end of the execution for a specific task. My implementation is the following:
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
spinner.center = self.imageView.center;
[self.imageView addSubview:spinner];
[spinner startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIImage *filteredImage = ...some filtering...;
self.imageView.image = filteredImage;
[self.imageView setNeedsDisplay];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner stopAnimating];
});
});
However, when I run the application, the spinning wheel disappears and after a while the image is updated. Do you have any hint about how there is this delay?
EDIT: the setNeedsDisplay and the stopAnimating instructions are called in the right order. However, the UIImageView takes a while in order to update its content.
Thank you in advance.
The problem with your code is-
You are trying to assigning image on imageview on other thread instead of main thread (GUI thread) so it takes time.
To resolve this I have made few changes in your code-
Instead of using this-
UIImage *filteredImage = ...some filtering...;
self.imageView.image = filteredImage;
[self.imageView setNeedsDisplay];
Try this-
self.imageView.image = filteredImage;
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = filteredImage;
[self.imageView setNeedsDisplay];
});
I have also faced this, hope it helps you.

UIImage+AFNetworking doesn't work

I have an image link, now i want to load this image in my app and i using category
[test setImageWithURL:[NSURL URLWithString:#"close"]];
link image is:
close
i see that link image is quite ok, but imageview can not load the image. Please tell me why, thanks!
ok try this
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void) {
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://216.57.222.146:8080/Stafflink_Web/images/assets/5108029/5108029_0_resized.jpg"];
UIImage* image = [[UIImage alloc] initWithData:imageData];
if (image) {
dispatch_async(dispatch_get_main_queue(), ^{
if (cell.tag == indexPath.row) {
test.image = image;
}
});
}
});
Is test a UIImageView object? It needs to be.
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
[imageView setImageWithURL:[NSURL URLWithString:#"http://i.imgur.com/r4uwx.jpg"] placeholderImage:[UIImage imageNamed:#"placeholder-avatar"]];

Where should i put my images in my iPhone project?

I'm new to iPhone dev.
Now i have a project and its directory structure looks like this :
Tutorial
\__Tutorial\
\__1.png
\__TutorialViewController.h
\__TutorialViewController.m
\__TutorialViewController.xib
\__Supporting Files\
\__Frameworks\
\__Products\
I tried to use [UIImage imageNamed:#"1.png"] to render an image to the view:
UIImage *image = [UIImage imageNamed:#"1.png"];
imageView.image = image;
[image release];
But the image doesn't shows up.
So i wonder where should i place the images ?
And additionally, if i got lots of images (like 1000 number), what should do ?
Here's my code :
#import "TutorialViewController.h"
#implementation TutorialViewController
#synthesize labelText;
#synthesize imageView;
-(void) click:(id)sender {
NSString *titleOfButton = [sender titleForState:UIControlStateNormal];
NSString *newText = [[NSString alloc]initWithFormat:#"%#",titleOfButton];
// Change Image When Clicking Color Button
if([titleOfButton isEqualToString:#"Blue"]){
NSLog(#"Blue");
imageView.image = [UIImage imageNamed:#"a.png"];
}else{
imageView.image = [UIImage imageNamed:#"b.png"];;
NSLog(#"Else");
}
labelText.text = newText;
[newText release];
}
You should create a folder named Resources and add images there.
UIImage *image = [UIImage imageNamed:#"IMG_0270.PNG"];
imgView_.image = image;
I had used the above code and it works fine on my system. May be you haven't connected the outlet for imgView in the interface builder.
EDIT:
The problem I found is you are not adding it as a subview. You should change your code like this :
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"1.png"]];
NSString *titleOfButton = [sender titleForState:UIControlStateNormal];
NSString *newText = [[NSString alloc] initWithFormat:#"%#", titleOfButton];
// Change Image When Clicking Color Button
if([titleOfButton isEqualToString:#"Blue"]) {
NSLog(#"Blue");
imageView.image = [UIImage imageNamed:#"IMG_0270.png"];
} else {
imageView.image = [UIImage imageNamed:#"b.png"];
NSLog(#"Else");
}
[imageView setFrame:CGRectMake(10, 10, 230, 123)];
[self.view addSubview:imageView];
// labelText.text = newText;
[newText release];
there is no prbm with ur code. but Some time image become corrupt , due to this reason it not show, try some other images , and if image is not added at bundle path add at
project target > build phase > copy bundle Resources
. might be it will help.
You have problem at this statement
[image release];
The easiest way to remember when to use release is NARC keyword :)
Always remember to make release call in 4 cases.
N New
A Alloc
R Retain
C Copy
Try to remove the first line of your method, if you've connected the UIImageView in the xib you don't need to re-alloc it. Moreover call self.imageView.image instead just imageView.image.

Loading image to UIViewController Asynchronously - With Code

This is written in my viewDidLoad method
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *op = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(downloadImage)
object:nil];
[queue addOperation:op];
// The the rest in other methods;
- (void)downloadImage{
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:person.picURL]];
UIImage* image = [[UIImage alloc] initWithData:imageData] ;
[self showImage: image];
}
I have tried adding the following codes above [self showImage: image]; and i ended up with an exception.
1.) [self performSelectorOnMainThread:#selector(showImage:) withObject:image waitUntilDone:NO];
2.) [self performSelectorInBackground:#selector(showImage:) withObject:image];
// This is the showImage code.
- (void)showImage:(UIImage *)img {
if (img != nil)
{
myImageView = [[UIImageView alloc] initWithImage:img];
myImageView.frame = CGRectMake(20, 20, 132, 124);
[scrollView addSubview:myImageView];
[pictureOfPerson setImage:img];
}
}
I am trying to Asynchronously download the image and cache it. The image gets downloaded Asynchronously but i am not sure if it gets cached.
1.) How to cache the image and use it in when the view loads again
2.) While the image is downloading, what if i click on another view. Then i need to stop the download. How can i write this code. I know that i have to write it in the viewDidDissapear method.
3.) Is my code correct. Have i missed anything or is there a better approach to do this? if so tutorial or some sample code please
I'd use GCD to download the image. It's much simpler to use than NSOperation. Here's an example:
UIImage *personPicture;
personPicture = [self.imageCache objectForKey:person.picURL];
if (!personPicture) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:person.picURL]];
personPicture = [UIImage imageWithData:imageData];
[self.imageCache setObject:imageData forKey:person.picURL];
dispatch_async(dispatch_get_main_queue(), ^{
[self showImage:personPicture];
});
});
}
else {
[self showImage:personPicture];
}
You could use a class property like an NSMutableDictionary to store the UIImage data and associate it with your URL. If it's present, use it, if not, download the image.
-EDIT-
Adding the code to handle caching.
You can use this:SDWebImage

activity indicator is not display

-(IBAction)actionPrevious:(id)sender{
[self startact];
pageNumber = pageNumber - 1;
if (pageNumber>0) {
NSString *str_Img =[array_Image objectAtIndex:pageNumber];
NSData *mydata = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:str_Img]];
UIImage *myimage = [[UIImage alloc] initWithData:mydata];
[imageView1 setImage:myimage];
[self.view addSubview:imageView1];
lbl_PhotoName.text = [array_Name objectAtIndex:pageNumber];
lbl_PhotoDate.text = [array_Date objectAtIndex:pageNumber];
lbl_PhotoDesc.text = [array_Desc objectAtIndex:pageNumber];
[mydata release];
[myimage release];
}
[self endact];
}
-(void)startact{
[act setHidden:NO];
[act startAnimating];
}
-(void)endact{
[act stopAnimating];
[act setHidden:YES];
}
In above code activity activity indicator is not display. Photo are display using the web service. please Help!
Thank You
You need to work on the same thread and need to call by this way
[self performSelector:#selector(startact) withObject:nil afterDelay:1];
You need to use threading in these kinds of scenarios.
Because activity indicator is on same thread as of the images work; thats why it is creating problem.
This is a silly mistake that I always seem to make: If you added the activity indicator programmatically did you make sure to addSubview: ? Or maybe it's hidden by something? Everything else looks fine, and you definitely don't need to startAnimating in a separate thread.