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.
}];
Related
Good afternoon I have a difficulty using the "Activity indicator", I want it to appear in my view while I do the download of an XML into an NSData and when the download finished it becomes invisible.
tried, but the indicator only appears after the download is finished.
The code I used is simple, start the "Activity indicator" call the server URL, transfer to NSData and then stop the "Activity indicator" and call another View that presents the information in a WebView, and that is when the "Activity indicator" starts to load, I want the display to appear before loading the WebView appears.
You need to download on a different thread (not the main thread). Best way is to use GCD. Here is the sample code:
//Start Activity indicator on the main thread,
[activityIndicator performSelectorOnMainThread:#selector(startAnimating) withObject:nil waitUntilDone:YES];
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Start Download code
dispatch_async( dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
});
});
This is an example for UIActivityIndicatorView. It starts the spinner just before starting to download and stops the spinner when the block's completion handler is called. Remember to update the UI only from the main queue - in this case stopping the spinner.
-(void)test
{
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activityIndicator setCenter:CGPointMake(screenWidth/2.0, screenHeight/2.0)];
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
NSMutableString *myURL = [[NSMutableString alloc] initWithString:#"http://www.domain.com/"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[myURL stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection
sendAsynchronousRequest:urlRequest
queue:queue
completionHandler: ^( NSURLResponse *response,
NSData *data,
NSError *error)
{
if (error == nil)
{
// do whatever with data
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating]; // stop spinner from the main queue
[activityIndicator removeFromSuperview];
});
} else // got an error
{
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating]; // stop spinner from the main queue
[activityIndicator removeFromSuperview];
});
}
}];
}
I'm using SDWebImage library for caching image in my iOS app. Now when loading image from server I'm showing an activity indicator on the imageview. I'm using the following code
__block UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.center = self.serviceImageView.center;
activityIndicator.hidesWhenStopped = YES;
[activityIndicator startAnimating];
[self.serviceImageView addSubview:activityIndicator];
[self.serviceImageView setImageWithURL:[NSURL URLWithString:[self.service valueForKey:#"image"]] placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
[activityIndicator removeFromSuperview];
}];
It works fine generally. But for some imageurls it didn't load the image and so it never comes to completed block & the activity indicator is spinning infinite.
So is there any way to set a timeout so that after a certain amount of time it gives me an error or something like that so I can stop the activity indicator.
It's bit argent. Please help.
You should check if the URL is valid, because when you pass nil to setImageWithURL the completion block won't be called.
NSURL* url = [NSURL URLWithString:[self.service valueForKey:#"image"]];
if (url)
[self.serviceImageView setImageWithURL:url placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
[activityIndicator removeFromSuperview];
}];
I
How can i download an asynchronous multiple images in the UITableView using ASIHttpRequest or something useful?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
..........
// Creation
UIImageView *avatar;
UILabel *content;
// Tag the IBOutlets
avatar = (UIImageView*)[cell viewWithTag:14];
content = (UILabel*)[cell.contentView viewWithTag:4];
// Field
avatar.image = image
content.text = entryReviewtableView.content;
}
No need to introduce a dependency to a whole framework such as ASIHTTPRequest just to download one image, when you can do it a few easy lines of code using GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *imageDate = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:imageData];
dispatch_async(dispatch_get_main_queue(), ^{
avatar.image = image;
});
});
This is asynchronous and all the goodness. But in a few lines of code you can write, understand, bug-fix, extend and maintain yourself.
You can be use the asynchronous image view instead of the default image view. for reference you can visit tutorial Here.
UIImageView *imgV=[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1024, 748)];
ASIHTTPRequest *req=[ASIHTTPRequest requestWithURL:[NSURL URLWithString:[Array objectAtIndex:indexPath.row]]];
[req setUsername:[NSString stringWithFormat:#"%i",i]];
[req setUserInfo:[NSDictionary dictionaryWithObjectsAndKeys:imgV,#"imgV",nil]];
[req setDelegate:self];
[req startAsynchronous];
//[imgV setContentMode:UIViewContentModeScaleToFill];
[imgV setContentMode:UIViewContentModeScaleAspectFit];
//[imgV setClipsToBounds:YES];
[imgV setTag:kTagImageViewInScrollView];
[cell addSubview:imgV];
- (void)requestFinished:(ASIHTTPRequest )request {
[(UIImageView)[[request userInfo] valueForKey:#"imgV"] setImage:[UIImage imageWithData:[request responseData]]];
[(UIActivityIndicatorView*) [(UIScrollView*) [scr viewWithTag:([[request username] intValue]+1)] viewWithTag:kActTag] removeFromSuperview];
}
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];
}
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
}