UIActivityIndicatorView unresponsive - iphone

When a UIButton is triggered, a UIActivityIndicator is started, and then stopped when implementLogin finishes:
-(IBAction)loginButton {
NSLog(#"loginButton triggered");
// Checks the Fields are not empty
if ([sessionIdField.text length] != 0 && [usernameField.text length] != 0 ) {
NSLog(#"Beginning Spinner");
// Displays spinner
[activitySpinner startAnimating];
[self implementLogin];
// Displays spinner
[activitySpinner stopAnimating];
}
}
However at runtime, the spinner doesn't appear! I have set the spinner to 'hide when stopped'.
When I set the activity indicator to animate before the view loads, it appears as it should, so I'm guessing it has a problem with the UIButton... (Also, I'm using 'Touch Up Inside' for the button.)
It's a simple problem... Can anyone help?
Thanks

Whatever implementLogin is doing (making a network request, perhaps?), it's doing it on the main thread, which is likely blocking UI updates like spinner animation.
You could recode something like this:
[activitySpinner startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_PRIORITY_DEFAULT, 0), ^{
[self implementLogin];
dispatch_async(dispatch_get_main_queue(), ^{
// Stops spinner
[activitySpinner stopAnimating];
}
}
[Code is untested, but you get the idea.]
What is happening here is that you are dispatching the login task to the background, and the last thing that block will do is stop the spinner on the main thread (as a separate task).

Without more code, one can only guess the reason why it doesn't work.
I assume 'Beginning Spinner' output correctly(?)
If so, you probably didn't properly initialize the UIActivityIndicatorView.
Does it look like this?
activitySpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activitySpinner.hidesWhenStopped = YES;
[view addSubview:activitySpinner];

Related

setHidden: has unwanted delay

I currently have a really weird bug.
A method gets called, that is supposed to hide an UIActivityIndicatorView by stopping it (automatic hiding when stopped is enabled) and an UIImageView called badIndicator.
As a replacement it is supposed to show another UIImageView called goodIndicator.
[goodIndicator setHidden:NO];
[badIndicator setHidden:YES];
[refreshIndicator stopAnimating];
NSLog(#"statussetting good should be completed");
The console prints the following right away, but it takes about three seconds for the changes to happen on the screen.
2013-05-31 20:24:57.835 app name[5948:1603] statussetting good should be completed
I have tried calling the setNeedsDisplay method on the objects and on the parent view and also replace hidden with alpha.
Still get the same problem.
It sounds like you are calling this from a background thread. All interaction with UIKit needs to happen from the main thread. Try using:
dispatch_async(dispatch_get_main_queue(), ^{
[goodIndicator setHidden:NO];
[badIndicator setHidden:YES];
[refreshIndicator stopAnimating];
NSLog(#"statussetting good should be completed");
});
you need to call this method in main thread.Try using:
-(void)hideControls {
[goodIndicator setHidden:NO];
[badIndicator setHidden:YES];
[refreshIndicator stopAnimating];
NSLog(#"statussetting good should be completed");
}

How to refresh display before returning to run loop

I am trying to display a message in the foreground by calling drawRect: or by adding a view that appears in foreground. The message must be displayed after pressing a button. I have noticed that the message is displayed only if the function that is called when button is pressed had exited.
- (IBAction)testBtnPress:(id)sender {
TestView *testView = [[TestView alloc] initWithFrame:self.view.frame];
[self.view addSubview:testView];
// processing to be done while the message is displayed
}
The drawRect: is called in the TestView class.
The rectangle with message is displayed after the processing to be done is finished, which is not convenient for my application.
Is there a way to display the message while the processing in running (the message will be "Processing... Please Wait").
Thanks.
The processing must either be done in a background thread, or performed in the next iteration of the runloop, to give your UI changes time to be handled.
You can do the processing in the next iteration of the runloop with something like this:
TestView *testView = ... ;
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
// processing to be done while the message is displayed
}];
This should work:
dispatch_async(dispatch_get_main_queue(), ^{
TestView *testView = [[TestView alloc] initWithFrame:self.view.frame];
[self.view addSubview:testView];
});

How to consume UI Events while loading data in the background

I have a little iPhone app that loads data from a web service. To make sure that nothing goes wrong while loading the data I create a semi-transparent view over the app and use CFRunloopRun() to wait until all the data is loaded in the background. This is the code for that:
self.connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self] autorelease];
// Now show an animation
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIView *window = [[UIApplication sharedApplication] keyWindow];
UIView *shield = [[UIView alloc] initWithFrame:window.bounds];
shield.backgroundColor = [UIColor blackColor];
shield.alpha = 0.5f;
[window addSubview:shield];
spinner.center = shield.center;
[shield addSubview:spinner];
spinner.hidden = NO;
NSLog( #"JCL.callServerWithRequest(), spinner view: %#, shield view: %#, window: %#", spinner, shield, window );
[spinner startAnimating];
// Hand over to the Runnloop to wait
CFRunLoopRun();
[spinner stopAnimating];
[spinner removeFromSuperview];
[spinner release];
[shield removeFromSuperview];
[shield release];
This works fine except that any clicks on a button somewhere is played after the loading so if the users clicks on the download button twice he will do the download twice as well.
Any idea how to consume the UI events before the shield is removed.
Thanks - Andy
Try it without messing with runloops. I suspect that the UI events are coming in to the normal loop on the window but not being processed until your custom loop returns, at which point the "shield" view is no longer there to catch them. If you put the shield in place and then let the main runloop handle things, the shield should catch them all as normal.
Thanks to Anomie I finally tried out to go without the CFRunLoopRun() and it is quite difficult because the execution is split into two parts: - the Invocation and the Return of the Result through a callback. But then I shot myself in the proverbial foot because I tried to block the returning thread to slow down the execution which did not work because that was executed again in the main thread.
Eventually I did slow down the Web Service and then everything worked as expected.

Activity Indicator when integrated into Searchbar does not display in iPhone SDK

In my iPhone app, I want to add activity indicator on top of a searchbar.
When it is searching it should display activity indicator.
I have added the activity indicator in XIB and created its outlet.
I am making it hide when the searching finishes, but Activity Indicator does not display.
Problem
I figured out that search function(say A)(where I animate the activity indicator) in turn calls another function(say B) so the main thread is being used in executing the function B. But for activity indicator to animate we require the main thread.
So I tried calling function B using performSelectorInBackGround:withObject method. Now when I click search the activity indicator is shown but the functionality of function B does not execute.
What can be a work-around for this?
There is not quite enough in your question to go on, but to start debugging, I would do the following.
Verify that the activity variably is really wired to the UIActivityIndicator you are creating in IB. (I would set a breakpoint on the setHidden: lines and make sure the variable is not null. Or throw an NSAssert(activity,#"Whoops! actity is null"); in there.)
If the variable is indeed set, I would start checking that it is in the right place in the view hierarchy. (I'd try doing a [self.view addSubview:activity] and see that it appears. You might have to replace it somewhere else.)
You might also want to try having it on by default in IB, until you have everything figured out.
Good Luck. Hope this helps.
Save yourself the hassle of creating a custom activity indicator and use the standard one that's available for you already - in the top bar. Also, IMO users tend to expect that one to spin when something is happening.
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
Obviously, set it to NO when your activity is over.
First of all, make sure you have #synthesize activity at the top of your .m file. Then in the viewDidLoad method, type activity.hidesWhenStopped = TRUE;. Next, in the method that is called when the search starts, type [activity startAnimating]; and [activity stopAnimating]; in the method when the searching stops.
try this:
set hidesWhenStopped = NO, so that is displayed all the time and then hide and show it manually. But the View should be set in IB to hidden first.
- (void)startActivityView {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
activity_view.hidden = NO;
[pool drain];
}
- (void)stopActivityView {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
activity_view.hidden = YES;
[pool drain];
}
- (void)doSomething {
[self performSelectorInBackground:#selector(startActivityView) withObject:nil];
// do some time consuming work
[self performSelectorInBackground:#selector(stopActivityView) withObject:nil];
}
Perhaps you have a view in front of your activity indicator? What if you always bring it to the front....
loadView = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
loadView.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
loadView.center = window.center;
loadView.opaque = NO;
[window addSubview: loadView];
[window bringSubviewToFront:loadView];
[loadView startAnimating];
I suggest that you use DSActivityView for showing your activity indicator. The source code can be found at Dejal blog.
Showing, and hiding, the activity view is a simple line of code.
[DSActivityView activityViewForView:self.view];
start animating the activity indicator and with a delay of 0.1 or 0.2 just call the other method u want.... i tried and it is working for me....
I have got the solution and it is as follows.
I just wrote the below line in Search button click event.
[NSThread detachNewThreadSelector:#selector(threadStartAnimating:) toTarget:self withObject:nil];
And defined the function threadStartAnimating: as follows:
-(void)threadStartAnimating:(id)data
{
[activityIndicator setHidden:NO];
[activityIndicator startAnimating];
}

Add view overlay to iPhone app

I'm trying to do something like this:
- (void)sectionChanged:(id)sender {
[self.view addSubview:loadingView];
// Something slow
[loadingView removeFromSuperview];
}
where loadingView is a semi-transparent view with a UIActivityIndicatorView. However, it seems like added subview changes don't take effect until the end of this method, so the view is removed before it becomes visible. If I remove the removeFromSuperview statement, the view shows up properly after the slow processing is done and is never removed. Is there any way to get around this?
Run your slow process in a background thread:
- (void)startBackgroundTask {
[self.view addSubview:loadingView];
[NSThread detachNewThreadSelector:#selector(backgroundTask) toTarget:self withObject:nil];
}
- (void)backgroundTask {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// do the background task
[self performSelectorOnMainThread:#selector(backgroundTaskDone) withObject:nil waitUntilDone:NO];
[pool release];
}
- (void)backgroundTaskDone {
[loadingView removeFromSuperview];
}
Two potential problems spring to mind, both centred around how you've implemented the 'do something slow here' code.
First off, if it's locking up the main thread then it's possible the application's UI isn't being redrawn in time to display the view, i.e. Add Subview, tight loop/intensive processing tying up the main thread, then immediately after the view is removed.
Secondly if the 'something slow' is being done asynchronously, then the view is being removed while the slow processing is running.
One things for sure, your requirements are as follows:
Add a subview to display some kind of 'loading' view
Invoke a slow running piece of functionality
Once the slow running functionality completes, remove the 'loading' subview.
- (void)beginProcessing {
[self.view addSubview:loadingView];
[NSThread detachNewThreadSelector:#selector(process) toTarget:self withObject:nil];
}
- (void)process {
// Do all your processing here.
[self performSelectorOnMainThread:#selector(processingComplete) withObject:nil waitUntilDone:NO];
}
- (void)processingComplete {
[loadingView removeFromSuperview];
}
You could also achieve something similar with NSOperations.