activity indicator before webView - iphone

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];
});
}
}];
}

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.
}];

SDWebImage multiple image download issue

I need images from URLs in my app and I also need to cache them for reuse.
Here is my code to download various images from various URLs:
__block UIActivityIndicatorView *activityIndicator = [UIActivityIndicatorView.alloc initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[imgView addSubview:activityIndicator];
activityIndicator.center = CGPointMake(65, 65);
[activityIndicator startAnimating];
[imgView setImageWithURL:imageURL placeholderImage:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)
{
if (error)
NSLog(#"%#", [error description]);
[activityIndicator removeFromSuperview];
activityIndicator = nil;
}];
At a time I fire 4 requests. Out of them, only 3 gets the actual image. Remaining 1 image does not download and activityIndicator keeps animating forever.
Is there something I am missing in above code? I have copied this from their own example that comes with the SDK.

iOS set timeout for url request in SDWebImage

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

UIActivityIndicatorView for an UIImage that load for an URL

I have an UIImage that is loaded from a URL, and i want to show an UIActivityIndicatorView animating while the image loads. Someone can help me?
Thanks for all.
So u want to use the UIActivityIndicator while accessing the server.
First create the indicator while the connection call, and when connection didfinishloading remove the activity indicator
//call this when connection start
UIActivityIndicator *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(140, 236, 37, 37);
[activityIndicator startAnimating];
[self.view addSubview:activityIndicator];
self.view.userInteractionEnabled = NO;
//remove activity indicator while connection did finish loadin
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[activityIndicator stopAnimating];
[activityIndicator removeFromSuperview];
self.view.userInteractionEnabled = YES;
}
First of all add Activity indicator in xib and connect object of the UIActivity Indicator.
Click "Hides when Stopped" in the xib.
Then,
-(void)FetchFromServer
{
NSURLRequest* updateRequest = [NSURLRequest requestWithURL: [NSURL URLWithString:#"Write Your Url Here"]];
NSURLConnection* connection = [[NSURLConnection alloc] initWithRequest:updateRequest delegate:self];
[connection start];
[activityIndicator startAnimating];
}
And then in the delegate,
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
UIImage *img = [UIImage imageWithData: data];
myImageView.image=img;
[activityIndicator stopAnimating];
}

UIButton image asynchronously

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