Loading image to UIViewController Asynchronously - With Code - iphone

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

Related

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"]];

iOS addSubview & removeFromSuperview causing memory leaks

I have the following issue:
I have one instance from class that extends UIViewController class, in this class i am adding UIImage to UIImageView, and then adding the UIImageView to the self.view using this method "addSubview:" [self.view addSubview:imageView] and finally adding the self.view to the window using [[[UIApplication sharedApplication] keyWindow] addSubview:[self view]] , and when i click on a close button i am clearing the self.view (removing the subview "UIImageView") and removing the self.view from the window.
now the issue is when i am using the same instance to add other UIImageView with other image, the first data is not freed from the memory even i have released them and removed them from the superview. the following code illustrates the problem.
- (void) setImageWithURL: (NSString *)url {
NSData * imageData =NSData * imageData =[NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
if (imageData) {
UIImage *image = [[UIImage alloc]initWithData:imageData];
[[[UIApplication sharedApplication] keyWindow] addSubview:[self view]];
[self createViewWithImage:image];
[image release];
}
-(void) createViewWithImage: (UIImage *) image{
[self.view setHidden:false];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self.view addSubview:imageView];
[imageView release];
}
// for removing the imageView when i click on close button
-(void) closeView{
for (UIView *subView in self.view.subviews) {
[subView removeFromSuperview];
}
[self.view removeFromSuperview];
}
Note: I have used the xcode instruments to test the memory allocation.
UPDATE:
after more research .. it is not addSubview & removeFromSuperview that make memory leaks.. its NSData. I dont why its not released from the memory. I have made some changes to test if its truly the NSData, i saved the image locally and set the image using two cases:
//Case 1:
NSString *path = [[NSBundle mainBundle] pathForResource:#"image" ofType:#"png"];
NSData * imageData =[[NSData dataWithContentsOfFile:path];
image = [[UIImage alloc]initWithData:imageData];
//Case 2:
NSString *path = [[NSBundle mainBundle] pathForResource:#"image" ofType:#"png"];
image = [[UIImage alloc] initWithContentsOfFile:path];
I have found that case one makes memory leaks, while case 2 is clean.
now UIImage cant take the image without using the NSData, and for some reason NSData is not released .. so how can i fix this problem !!!!
Do you have a:
[super dealloc];
in your deriving class's dealloc?

The most common way to load UIImage from the web

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.

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.

Custom UIView Leaking Memory - iPhone/iPad/iOS App Development

I have a viewcontroller that repeatedly repositions 6 items within a uiscrollview. However, even though I've limited the number of items within the uiscrollview to 6, I'm still leaking memory when i update their position and their image. Can someone let me know if the following code which represents a unit within the uiscrollview is properly coded? startLoad is the method that I call after to reload the image.
#import "ScrollUnit.h"
#implementation ScrollUnit
#synthesize index;
#synthesize ProductToDisplay;
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
-(void)startLoad
{
[imageview removeFromSuperview];
[imageview release];
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(loadImage)
object:nil];
[queue addOperation:operation];
[operation release];
}
-(void)loadImage
{
NSString *myimageName = [self.ProductToDisplay valueForKey:IMAGEKEY];
NSString *myimageUrl = [NSString stringWithFormat:#"%#/%#",IMAGE_SERVICE,myimageName];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:myimageUrl]];
UIImage* image = [[[UIImage alloc] initWithData:imageData] autorelease];
[imageData release];
[self performSelectorOnMainThread:#selector(displayImage:) withObject:image waitUntilDone:NO];
}
- (void)displayImage:(UIImage *)image
{
imageview = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 0, 320, 320)]retain];
imageview.contentMode = UIViewContentModeScaleAspectFit;
imageview.backgroundColor = [UIColor whiteColor];
imageview.image = image;
[self addSubview:imageview];
[imageview release];
//[image release];
}
- (void)dealloc {
[super dealloc];
}
#end
Get rid of the retain message on this line and you should be all set:
imageview = [[[UIImageView alloc] initWithFrame:CGRectMake(9, 0, 320, 320)]retain];
The reason why you need to do this is because you already own the object by calling alloc, so at that point, the relative reference count is 2.
Once you invoke addSubview:, passing in the imageview, the reference count gets bumped to 3, then right back down to 2 once you release it on the next line.
So once that object gets sent release in -dealloc, you're still stuck because the reference count is now 1, not 0 as you expected.
There is also another little thing that might bug (or not). You don't release imageview before you assign it in displayImage method. As long as only startLoad is called you are fine but if displayImage is called from the outside then you still leak.
You might want to use a property with retain and then synthesis the getter and setter methods. This way the iOS will release your previous assignment before it retains your new assigned object. That said then you need to release created image view right were you create it and you have to use "self.imageview" in order to make sure that you use the setter (setImageview).