UIButton image asynchronously - iphone

I have a bunch of UIButton in a UIScrollView and each UIButton takes an image from a URL. What is the easiest way so that the image will be loaded asynchronously?
For example, the usual way is:
[button setImage:[UIImage imageWithData:data] forState:UIControlStateNormal];
but this blocks the UI, I don't want it to

You can try this
[self performSelectorInBackground:#selector(loadImag) withObject:nil];
In loadImage function load the image from url and then assign it to the button.
I am not sure this will work for you...As I am a beginner in objective C development

Try with an NSInvocationOperation, make an synchronous request for each image button... Pass the button as parameter, something like this i mean...
Init the operation queue (maybe on init):
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
start the operation invocation for each button...
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(getImageFromURL:)
object:[NSDictionary dictionaryWithObjectsAndKeys:#"http://getMyImage.com/resource.jpg", #"url", button, #"button", nil]];
[queue addOperation:operation];
[operation release];
this can be your getImageFromURL: selector
- (void) getImageFromURL:(NSDictionary*)dict
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLFromString:[dict objectForKey:#"url"]];
UIButton *button = [dict objectForKey:#"button"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
UIImage *image = [[UIImage alloc] initWithData:data];
// Finally set the button image and release image ...
[pool drain];
}
Don't forget release queue on dealloc...
Hope this helps! :)

I guess it is alright, if you block while loading one single image? The problem is that you have many of them? If so, then I would do like this (no threads needed):
#define DELAY 0.1 // you may set it to 0 as well
...
[self performSelector:#selector(setupButton:)
withObject:[NSNumber numberWithInt:0]
afterDelay:DELAY];
...
-(void)setupButton:(NSNumber*)count
{
UIButton *button = [self buttonFromMyScrollViewWithCount:count.intValue];
[button setImage:...];
if (count.intValue < self.numberOfButtonsInMyScrollView)
[self performSelector:#selector(setupButton:)
withObject:[NSNumber numberWithInt:count.intValue + 1]
afterDelay:DELAY];
}

Related

How to download and set a UIImage not on the main thread

I'm adding an image from a web service. How would I code this so that it's not on the main thread? I want the view to load first and then the image to load so that the user doesn't experience any slowness when the detail view controller loads.
What I'm unsure of is how to add this into the dispatching code.
here's my code so far:
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:mainImageUrl]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(10, 150, 300, 180)];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, 300, 180)];
[iv setImage:image];
[_scrollView addSubview:v];
[v addSubview:iv];
and this is what I'm thinking I can use for the threading:
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Add code here to do background processing
//
//
dispatch_async( dispatch_get_main_queue(), ^{
// Add code here to update the UI/send notifications based on the
// results of the background processing
});
});
thanks for the help
You can use the GCD to download image like this.
//Your ImageView
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 10, 300, 180)];
// Create a __block type reference for the variable, for ARC use __weak instead
__block UIImageView *blockimage = iv;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *imageData=[NSData dataWithContentsOfURL:[NSURL URLWithString:mainImageUrl]];
dispatch_sync(dispatch_get_main_queue(), ^{
blockimage.image = [UIImage imageWithData:imageData];
// To avoid retain cycle
blockimage = nil;
});
});
This code is downloading the image synchronously within GCD async block by using dataWithContentsOfURL: API, disadvantage is you cannot get the precise information when image fails to download. In this case to properly handle error situations, use NSURLConnection to asynchronously download the image data.
Also, as suggested correctly in few of answers to your question, you can use SDWebImage or AFNetworking image view categories. This will give you image cache facility and ensure that the image is downloaded asynchronously without affecting the main thread performance.
Hope that helps!
Or you could use the AFNetworking category on UIImageView to do that !
Use SDWebImage to download image on secondary thread. It will load image in UIImageView on sencondary thread. Import SDWebImage package and use this code
here is the code
#import "SDWebImage/UIImageView+WebCache.h"
[yourimageView setImageWithURL:[NSURL URLWithString:#"your image Url"] placeholderImage:[UIImage imageNamed:#""]];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://www.hdwallpapersbest.com/wp-content/uploads/2012/12/Beauty-of-Nature-Awesome-Images-01-21.jpg"]];
[NSURLConnection sendAsynchronousRequest:req queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *res, NSData *data, NSError *err) {
[_imageView setImage:[UIImage imageWithData:data]];
}];
Create NSOperationQueue and create NSOperation.You will have to make a nsmutableurl request using NSOperation.It will start image downloading in background.After your work is finished you have to move to main thread using performselector on mainthread.
[super viewDidLoad];
// Create a new NSOperationQueue instance.
NSPerationQueue *operationQueue = [NSOperationQueue new];
// Create a new NSOperation object using the NSInvocationOperation subclass.
// Tell it to run the counterTask method.
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(counterTask)
object:nil];
// Add the operation to the queue and let it to be executed.
[operationQueue addOperation:operation];
[operation release];
// The same story as above, just tell here to execute the colorRotatorTask method.
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
selector:#selector(colorRotatorTask)
object:nil];
[operationQueue addOperation:operation];
[operation release];
I would do as suggested run you code using dispatch_async setting the variables to use a background thread. Then use [self performSelectorOnMainThread:#selector(setImage:) withObject:image waitUntilDone:NO]; to update the UI on the main thread
The code you have proposed looks like the correct way to do it using GCD. However, in this specific case (since you're loading a URL) I suggest you take a look at NSURLRequest and NSURLConnection because these classes do it for you and provide a good framework for setting up caching policies, handling errors, etc.
Here is some example code:
NSURLRequest* request = [NSURLRequest requestWithURL:yourNSURLObject];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error)
{
// The data is now available in the 'data' object.
UIImage* image = [UIImage imageWithData:data];
// Do whatever with the image.
}];

Parse image issue in Iphone

I have a array which have a one content image URL for key "Image". I want to show that image in my table view.
How to do it .Please give me suggestion.
You can search in google using "lazy loading image for tableview".
It is also available with apple example.
img_view = [[UIImageView alloc]init];
img_view.autoresizingMask = UIViewAutoresizingFlexibleWidth;
img_view.frame = CGRectMake(0,0,130, 75);
activityIndicator = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
[img_view addSubview: activityIndicator];
activityIndicator.center = CGPointMake(50,30);
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(loadImage:)
object:[[xmlParser data1] objectAtIndex:indexPath.row]];
[queue addOperation:operation];
[operation release];
[cell.contentView addSubview:img_view];
-(void)loadImage:(Book *)book
{
[activityIndicator startAnimating];
NSString *imgurl=[book image_url];
NSURL *url=[[NSURL alloc]initWithString:imgurl];
NSData *img=[[NSData alloc]initWithContentsOfURL:url];
UIImage *image=[UIImage imageWithData:img];
img_view.image=image;
[activityIndicator stopAnimating];
}

Error in Load image in NSThread?

I have a url which contain image address, i want to load that image via NSThread but i am facing problem. I am doing thing like this.
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 10, 55, 57)];
[NSThread detachNewThreadSelector:#selector(showImage) toTarget:self withObject:nil];
[self.view addSubview:imageView];
- (void) showImage {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSURL *url = [NSURL URLWithString:temp.strUrl];
UIImage *chart = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
[url release];
imageView.image = chart;
[pool release];
}
Please help me on that.
the problem is here.
[url release];
you are not supposed to release the url object . as you haven't alocated it, may be that is what you are facing problem with.

Slow speed while converting NSDATA to UIImage

I have some images in the array in the form of NSDATA.and i am showing them on page in page controller example 6 at a time.
But they are taking time to get convert in to UIImage that's why scrolling get slow dowwn is we have any other option?
i am using following code to convert them into NSDATA.
[UImage imageWithData:data];
From http://www.switchonthecode.com/tutorials/loading-images-asynchronously-on-iphone-using-nsinvocationoperation :
In your main thread:
NSOperationQueue *queue = [NSOperationQueue new];
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(loadImage)
object:nil];
[queue addOperation:operation];
[operation release];
Other methods required:
- (void)loadImage {
NSData* imageData = //however you're getting your NSData
UIImage* image = [[[UIImage alloc] initWithData:imageData] autorelease];
[imageData release];
[self performSelectorOnMainThread:#selector(displayImage:) withObject:image waitUntilDone:NO];
}
- (void)displayImage:(UIImage *)image {
[imageView setImage:image]; //UIImageView
}

stopping a photo from a new thread to load

I'm loading images with this class. I need help on how to stop them from loading when dealloc is called. This class is extending UIImageView. Also any other advice for designing the class is appreciated, i'll want for example to dispatch the loaded bytes.
#implementation RCPhoto
- (id)initWithFrame:(CGRect)frame delegate:(id)d {
if ((self = [super initWithFrame:frame])) {
// Initialization code
delegate = d;
self.contentMode = UIViewContentModeScaleAspectFit;
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#pragma mark Load photo
- (void)loadImage:(NSString *)path {
// Create a new thread for loading the image
[NSThread detachNewThreadSelector:#selector(load:)
toTarget:self
withObject:path];
}
- (void)load:(NSString *)path {
// You must create a autorelease pool for all secondary threads.
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//NSLog(#"RCPhoto loadImage into a new thread: %#", path);
NSURL *url = [NSURL URLWithString: path];
NSData *data = [NSData dataWithContentsOfURL: url];
UIImage *image = [[UIImage alloc] initWithData: data];
[self performSelectorOnMainThread: #selector(performCompleteEvent:)
withObject: image
waitUntilDone: NO];
[pool release];
}
- (void)performCompleteEvent:(UIImage *)image {
//NSLog(#"RCPhoto finish loading");
[self setImage:image];
self.opaque = YES;
if ([delegate respondsToSelector:#selector(onPhotoComplete)]) {
[delegate performSelector:#selector(onPhotoComplete)];
}
}
#end
dataWithContentsOfURL: is designed to block until it times out or finishes receiving a complete response. You seem interested in interrupting the background thread when its associated view is deallocated. These facts are fundamentally at odds.
If you want to terminate the request as quickly as possible when your object is going away, this can be done by making an asynchronous request with NSURLConnection and canceling it in your dealloc method. If it isn't canceled before the response is received, you'll get a callback to connectionDidFinishLoading: on your delegate, at which point you can reconstitute your UIImage from the received data.
Final code:
- (void)loadImage:(NSString *)path {
imageData = [[NSMutableData data] retain];
NSURL *url = [NSURL URLWithString: path];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)conn {
UIImage *image = [[UIImage alloc] initWithData:imageData];
[self setImage:image];
self.opaque = YES;// explicitly opaque for performance
[image release];
}