UIActivityIndicator issue when downloading from URL - iphone

I'm trying to download the picture from URL and use a activity indicator animating to present the fact that the file is downloading. However, it is not working as my indicator isn't animating when I call this download function, can somebody tell me why?
-(void)download{
[indicator startAnimating];
NSString *downloadPath=#"http://www.xyz.com/path/pic.jpg;
NSData *downloadData=[NSData dataWithContentsOfURL:[ NSURL URLWithString:downloadPath]];
if(downloadData){
//do something
[indicator stopAnimating];
}
else{
//do something
[indicator stopAnimating];
}
}

You need to put [indicator startAnimating] and [indicator stopAnimating]; in separate methods. I believe the animation does not kick in until the method reaches its end. So if you separate this into several methods this should work
One method that starts your animation
One method that downloads the file.
One method that stops the animation.
Another option is threading to acomplish this. More information here

The animation is performed in the event loop, which is in the same thread as your code. That is, the animation won't start while your code is executing.
Instead you either need to forget about the animation, switch to using the asynchronous download methods or perform the download in a separate thread. I'd recommend the async option.

Related

Objective C UIActivityView stops animating only after a delay

I am not sure why, but for some reason my UIActivityIndicator spinner stays visible for awhile after I ask it to stop spinning. In my app, I check for updates with a block method callback when the check is complete after which point the activity indicator is to be hidden. The NSLog is processed immediately when the block is called, but it takes maybe 5-10 seconds for the indicator to disappear. Nothing is going on in the main as far as I can tell, the app is just sitting there. I am very confused...
[self showActivityIndicator];
[[self schedulePack] checkForUpdates:^(void)
{
NSLog(#"Done updating.");
[self hideActivityIndicator];
}];
The problem is probably that you access a UI element from a separate thread. Try replacing
[self hideActivityIndicator];
with
[self performSelectorOnMainThread:#selector(hideActivityIndicator) withObject:nil waitUntilDone:NO];

Activity Indicator not showing up

I have two issues with activity indicator:
1. Activity Indicator not showing up on UIViewController
I have activity indicator added in .xib file. On button click it should start animating. and when response from server is received, before going to next page it should stop animating.
I am doing it as follows:
activityIndicator.hidden = NO;
[activityIndicator performSelector:#selector(startAnimating) withObject:nil afterDelay:0.1];
[self.view bringSubviewToFront:activityIndicator];
....rest of code here....
activityIndicator.hidden = YES;
[activityIndicator stopAnimating];
Activity Indicator not showing up on UITableView
For table view I am doing it same way but on didselectrowatindexpath...
For tableview I also tried adding activity view to cell accessory, but still not showing up
In both cases activity Indicator is not showing up.
Please help
Thanks
If all this code is in one method or in response to one event, then none of the changes to the views are going be visible until you return to the event loop. You set the activityIndicator.hidden to NO and then set it again to YES before the UI has an opportunity to even refresh.
You also apparently stop the animation before you start it.
What you need to do is make the activity indicator visible here and start its animation. Then schedule the work to be done (start an asynchronous network connection, or put some work into a queue, or whatever it is you need to get done) and return from this method so that the UI can refresh, the indicator can be drawn, and the animation can actually start.
Then later at some point after the work is complete, you can hide the indicator and stop the animation. But you can't do all of that on the main thread within one single turn of the event loop. None of your changes will be visible because no drawing at all will happen here while this method is executing (assuming this is on the main thread)
I hope that makes sense?
Now I modified the code to this:
activityIndicator.hidden = NO;
[activityIndicator startAnimating];
[self performSelector:#selector(saveClicked) withObject:nil afterDelay:0.1];
[self.view bringSubviewToFront:activityIndicator];
and it worked :)
May be, in tableView, instead of self.view , it will be self.navigationController.view ??

Doing a task that needs to update the screen

Imagine I have a spinner that I have to enable while something relatively heavy is being done and then deactivate the spinner after the task is done.
If I do:
[mySpinner startAnimating];
[self doSomethingHeavy];
[mySpinner stopAnimating];
I will never see the spinner running, because doSomethingHeavy will lock the thread and never let the spinner show.
I have tried to fire a new queue on the main thread using Grand Central Dispatch for the spinner and in another try for the task, but the results are the same. No spinner running.
The only way to make it work is to fire the method with a delay, using
[self performSelector:#selector(doSomethingHeavy) withObject:nil afterDelay:0.02];
but this sounds more like a hack and if I put [mySpinner stopAnimating] after that line, it will probably stop the spinner before the task is done.
This is not just valid for the spinner but for any task that that needs screen update.
...
[mySpinner startAnimating];
[self performSelectorInBackground:#selector(doSomethingHeavy) withObject:nil];
...
}
- (void)doSomethingHeavy {
...
[mySpinner performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:NO];
}
Or instead of stopping the spinner in doSomethingHeavy it would more likley finish with a call to:
[self performSelectorOnMainThread:#selector(finishedSomethingHeavy) withObject:nil waitUntilDone:NO];
which would stop the spinner and update the UI with the heavy results.
Hopefully I'm understanding the question properly, but normally I'd use the method
[self performSelectorInBackground:#selector(doSomethingHeavy) withObject:nil];
in this situation as this leaves the main thread free to update the UI while still performing the task in the background.
I have tried these solutions but unfortunately none worked, because my doSomethingHeavy method must be run on the main thread too.
Using the hack of firing the method with a delay works but not for methods that should run with more than one parameter, as performSelector: afterDelay: cannot be used to pass more than one parameter.
I found a solution firing a queue on the main thread using Grand Central Dispatch and then sleeping it for 0.02 seconds. Using the queue, I can put anything I want.

UIActivityIndicator not working properly?

I have a problem regarding UIActivityIndicator. I applied [spinner startAnimating] at the IBAction on a button and then doing some process. After the process activityindicator should be stopped and then navigate to another view. But the activity indicator does not appear. When I remove the line "[spinner stopAnimating]" then the indicator appears but not at the instant button is pressed. It appears just before the other view loads, and apparently does not appear, I mean it does not appear but if we see very carefully then only it appears for an instant.
Thanx in advance for any answer.
Ole is pretty much correct, but there is a trick of you don't mind synchronous processing (often that it why you want to display the activity indicator in the first place).
First move your code that you want to process while the spinner is up to its own method. Then do
[spinner startAnimating];
[self performSelector:#selector(methodname) withObject:nil afterDelay:0];
The afterDelay:0 means on the next time through the run loop. That way the spinner gets started.
The animation will not start until your code returns control to the run loop. If your processing task blocks the main thread, the no UI updates will take place until it is finished. You should do your processing asynchronously (e.g. by starting an NSOperation).
you should run in perform selector .
for ex:
[self performSelector:#selector(animation) withObject:nil afterDelay:0]
-(void)animation
{
NSAutoreleasepool *pool = [[NSAutorepleasepool alloc]init];
[indicatorView startAnimating];
[pool release];
}
This is an old question. I leaving my answer here, so that might help someone to solve their problem.

implementing UIActivityIndicatorView while NSData dataWithContentsOfURL is downloading

I am downloading an mp3 using NSData dataWithContentsOfURL:url. This takes a while and while the file is downloading the application hangs. I want to handle well and ideal would like to show the download progress but can't find methods for this.
It is in a UIViewController and I have made a first attempt by putting in a UIActivityIndicatorView and start it spinning before I start the download, then stop it spinning after but nothing appears.
So my question really is please could someone tell me what the best way to handle this is? Thanks so much
Nothing will appear because your main thread is blocked doing the download, and the main thread is where UI updates occur.
You should use NSUrlConnection to download asynchronously and implement the delegate methods to start/stop your spinner.
Alternatively if you want to stick with NSData's dataWithContentsOfURL:url you should do this on a separate thread and update the spinner on the main thread before and after you call it.
You can achieve this while still using synchronous methods, but you need to give the run loop a chance to start animating the activity indicator before you start the download.
You can achieve this by using either performSelector:withObject:afterDelay: with delay 0 to put a run loop between your animation start and the download, or (worse style, more risky) you can directly invoke the run loop within your code.
Sample code:
- (void)loadPart1 {
activityIndicator = [[[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIA...StyleGray]
autorelease];
activityIndicator.frame = myFrame;
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
[self performSelector:#selector(loadPart2) withObject:nil afterDelay:0];
}
- (void)loadPart2 {
[NSURLConnection sendSynchronousRequest:request returningResponse:&response
error:&error];
[activityIndicator stopAnimating];
}
More details here:
http://bynomial.com/blog/?p=15
(scroll down to Solution 1 or Solution 2).