UIActivityIndicatorView subview disappears before NSOperationQueue is finished - ios

I'm trying to get a UIActivityIndicatorView to show on screen while my CSV import method is running, but I can't get it right. With the code below, the ActivityIndicator subview shows for a second or so then disappears, even if the import operation is still running. How can I make it stay on screen until the NSOperationQueue is finished? I'm using iOS 7.1 on my test device.
User taps 'Yes' on an UIAlertView to import the data:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if(buttonIndex == 0){
//clicked Yes
[self loadingSpinner];
operationQueue = [NSOperationQueue new];
NSInvocationOperation *importOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(importCSVData:) object:self.importURL];
[operationQueue addOperation:importOperation];
}
else if(buttonIndex == 1){
//clicked No
}
}
Method to show a UIActivityIndicatorView on top of everything else on screen:
-(void)loadingSpinner{
self.overlayView = [[UIView alloc] init];
self.overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
UIView *topView = [UIApplication sharedApplication].keyWindow.rootViewController.view;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
self.overlayView.frame = [UIScreen mainScreen].bounds;
}
else{
self.overlayView.frame = topView.frame;
}
[self.overlayView setUserInteractionEnabled:NO];
self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.spinner.center = self.overlayView.center;
[self.overlayView addSubview:self.spinner];
[self.spinner startAnimating];
[topView addSubview:self.overlayView];
}
At the end of the import operation to remove the activity indicator:
[self.overlayView removeFromSuperview];

Use addOperationWithBlock: method on NSOperationQueue with which you will have more control. You would edit your code like this to use block version,
NSOperationQueue *queue = [NSOperationQueue new];
[queue addOperationWithBlock:^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self loadingSpinner];
}];
[self importCSVData:self.importUrl];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self.overlay removeFromSuperView];
}];
}];

You can also use GCD to implement this task.
Create dispatch queue:
dispatch_queue_t queue = dispatch_queue_create("Other Q", NULL);
or you can use the main queue like:
dispatch_get_main_queue()
Finally replace your code with the following:
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(),^{
[self loadingSpinner];
});
[self importCSVData:self.importURL];
});
You might want to check for the title of you button clicked on your UIAlertView.
To do so change this:
if(buttonIndex == 1)
With this:
if([[alertView buttonTitleAtIndex:buttonIndex]isEqual:#"YES"])
Let me know if this helps.

Related

UIActivityIndicator Not Showing After WebService Has Been Called

In my application, I need to show a UIActivityIndicator while waiting for response from web service. I am using the following code:
UIView *loadView = [[UIView alloc] initWithFrame:self.view.bounds];
loadView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.5];
//UIActivityIndicatorView *activityView = [[UIActivityIndicatorView alloc] init];
//[second.loadingView addSubview:activityView];
//activityView.center = second.loadingView.center;
//[second.view addSubview:second.loadingView];
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[loadView addSubview:activity];
activity.center = loadView.center;
[self.view addSubview:loadView];
[self.view bringSubviewToFront:loadView];
activity.hidesWhenStopped = YES;
[activity setHidden:NO];
[activity startAnimating];
// calling My web service
[self callRegisterWebService:self.userFname lastName:self.userLName email:self.userEmail];
[activity stopAnimating];
[loadView setHidden:YES];
However, the indicator does not show up..!! What am i doing wrong??
You are probably waiting for the http response on the main thread. Try to implement the registration with dispatch_async function like this:
[activity startAnimating];
// calling My web service
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self callRegisterWebService:self.userFname lastName:self.userLName email:self.userEmail];
[activity stopAnimating];
});

UIActivityIndicatorView does not show

I have an app with a table view controller in which a user selects a US state, a web service is called and data is displayed for that state in the destination table view controller. Since the web service can take some time to complete I want an activity indicator. Since there will be no temporary data to display, I need this to be processed synchronously. So my task is pretty simple: start the activity indicator, call the web service, and after it completes, stop the activity indicator.
I am obviously doing something wrong and no activity indicator ever displays.
Here is the code from my destination table view controller's viewDidAppear method:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[self.tableView bringSubviewToFront:spinner];
spinner.hidesWhenStopped = YES;
spinner.hidden = NO;
[spinner startAnimating];
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
[self.tableView reloadData];
[spinner stopAnimating];
}
Header:
#property (strong, nonatomic) UIActivityIndicatorView *spinner;
GaugeList is the object which makes the web service call.
Can someone tell me how to get an activity indicator view to appear? Thanks!
You forgot to add spinner on table view. Your code should look as follows:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spiner.center = //set some center
[self.tableView addSubview: spinner];
[self.tableView bringSubviewToFront:spinner];
spinner.hidesWhenStopped = YES;
spinner.hidden = NO;
[spinner startAnimating];
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
[self.tableView reloadData];
[spinner stopAnimating];
}
Also you send requests to a web service in main thread. This is bad practice. I would suggest something like following:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
spiner.center = //set some center
[self.tableView addSubview: spinner];
[self.tableView bringSubviewToFront:spinner];
spinner.hidesWhenStopped = YES;
spinner.hidden = NO;
[spinner startAnimating];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
stateGauges = [[GaugeList alloc] initWithStateIdentifier:stateIdentifier andType:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
[spinner stopAnimating];
});
});
}
At first you should add activity indicator to some view to show it. But you can not add it to UITableView, because UITableView is subclass of UIScrollView and you will see floating activity indicator. The best way in your case is to add activity indicator to navigation bar, etc. Or if you want to disable table view you should write something like this:
- (void)viewDidLoad {
[super viewDidLoad];
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
UIView *dummyView = [[UIView alloc] init];
dummyView.frame = self.tableView.bounds;
dummyView.alpha = 0.5f;
dummyView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
dummyView.userInteractionEnabled = YES;
dummyView.backgroundColor = [UIColor blackColor];
[dummyView addSubview:activityIndicator];
activityIndicator.center = dummyView.center;
[self.tableView addSubview:dummyView];
}
Try using self.spinner instead of using spinner.

show animation and locking the current view

I have a method which is calling a external webservice.
Now I want to show an UIActivityIndicatorView in the middle of the screen, while the webservice-call is running. I call the webservice-method asynchronously. But with my code which you will find at the bottom, I have two problems:
the current view is not locked. That means, that the user can interact with the current view. I want to block all till the animation is stopped
the animation is not centered
Here is my code which I have tried now:
-(void)saveDataOnServer {
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(225, 115, 30, 30)];
[activity setBackgroundColor:[UIColor clearColor]];
[activity setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleGray];
[self.view addSubview:activity];
[activity startAnimating];
// ...
[NSURLConnection sendAsynchronousRequest: request
queue: queue
completionHandler: ^(NSURLResponse *response, NSData *data, NSError *error) {
[activity stopAnimating];
// ...
}
];
}
You have to put another view when activity indicator start :
UIView *overlayView;
overlayView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
overlayView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
[[UIApplication sharedApplication].keyWindow addSubview:overlayView];
And remove that view(overlayView) when activity indicator stops :
[overlayView removeFromSuperview];
This OverlayView will cover current view.So user can not interact with current view.
Hi there i have a good solution for you ..
there i a library called MBProgressHUD
which does all you want , WITH Style
This is The link for the library HERE
And its easy to use .. here is an example i used
self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
self.hud.mode = MBProgressHUDModeIndeterminate;
self.hud.labelText = #"Fetching News";
//self.hud.detailsLabelText=#"Fetching News";
self.hud.dimBackground = YES;
and there is a demo you can see .. Cheers :D

Asynchronously load UIViews

I'm trying to load views asynchronously. The problem is that the frame of the views to be loaded depends on the data that's loaded asynchronously. In other words there are some long calculations that decide where to actually display the UIViews.
I know that there are problems when trying to actually display a UIView in a thread and that you should always load them back in the main thread, so this is the code I've been trying out:
asyncQueue = [[NSOperationQueue alloc] init];
[asyncQueue addOperationWithBlock:^{
// Do work to load the UIViews and figure out where they should be
UIButton *test = [[UIButton alloc] initWithFrame:[self doWorkToGetFrame]];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self addSubview:test];
}
}];
}];
This all resides in a UIView container.
Try something like this:
UIButton *test = [[UIButton alloc] initWithFrame:CGRectZero];
test.hidden = YES;
test.alpha = 0.0f;
[self addSubview:test];
dispatch_queue_t downloadQueue = dispatch_queue_create("myDownloadQueue",NULL);
dispatch_async(downloadQueue, ^
{
// do work to load UIViews and calculate frame
CGRect frameButton = ...;
dispatch_async(dispatch_get_main_queue(), ^
{
test.hidden = NO;
[UIView animateWithDuration:0.4f animations:^
{
test.frame = frameButton;
test.alpha = 1.0f;
}
completion:^(BOOL finished){}];
});
});
dispatch_release(downloadQueue);
This adds your button in the main thread, but makes it invisible initially. After your background work is done, you'll make it visible and set the frame using an animation.

Cant see the UIActivityIndicatorView during Call to Server for data

I'm writing an app that is calling a server to get data to show to the user as the app starts, the call to the server is a sync call, i want to show the user a UIActivityIndicatorView, but i cant see it Although i'm activating the UIActivityIndicatorView in a new Thread, hear is the code:
- (void)viewDidLoad {
[super viewDidLoad];
spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.frame = CGRectMake(50, 50, 50, 50);
[self.tableView addSubview:spinner];
[self.tableView bringSubviewToFront:spinner];
singeltoneData *sing = [singeltoneData sharedInstance];
firstTimeSearch = YES;
firstTimeSearchClick = YES;
NSNumber *num = [[NSNumber alloc]initWithInt:-1];
[NSThread detachNewThreadSelector:#selector(spin:) toTarget:self withObject:nil];
[self getData:num];
filterCalls = [[sing.globalCallsDitionary objectForKey:#"Calls"]mutableCopy];
allCalls = [[sing.globalCallsDitionary objectForKey:#"Calls"]mutableCopy];
callsDetails = [[sing.globalCallsDitionary objectForKey:#"CallDetails"]mutableCopy];
filteredCallsDetails = [[sing.globalCallsDitionary objectForKey:#"CallDetails"]mutableCopy];
#if defined(__IPHONE_5_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_5_0
if ([self.navigationController.navigationBar respondsToSelector:#selector( setBackgroundImage:forBarMetrics:)]){
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:#"top.png"] forBarMetrics:UIBarMetricsDefault];
}
#endif
[self buildBar];
woman = [UIImage imageNamed:#"woman.png"];
}
next my spin function looks like this
/************************************************************/
/* Spinner */
/************************************************************/
- (void) spin:(id)data{
[spinner startAnimating];
}
I'm also calling it from refresh data call to the server:
- (void)activateActions:(id)sender {
[NSThread detachNewThreadSelector:#selector(spin:) toTarget:self withObject:nil];
singeltoneData *sing = [[singeltoneData sharedInstance]autorelease];
[allCalls removeAllObjects];
[callsDetails removeAllObjects];
[filterCalls removeAllObjects];
[filteredCallsDetails removeAllObjects];
NSNumber *num = [[NSNumber alloc]initWithInt:-1];
[self getData:num];
filterCalls = [[sing.globalCallsDitionary objectForKey:#"Calls"]mutableCopy];
filteredCallsDetails = [[sing.globalCallsDitionary objectForKey:#"CallDetails"]mutableCopy];
allCalls = [[sing.globalCallsDitionary objectForKey:#"Calls"]mutableCopy];
callsDetails = [[sing.globalCallsDitionary objectForKey:#"CallDetails"]mutableCopy];
[self.tableView reloadData];
}
still i dont see the spinner
any help?
[NSThread detachNewThreadSelector:#selector(spin:) toTarget:self withObject:nil];
- (void) spin:(id)data{
[spinner startAnimating];
}
Based on above you are animating the spinner on a different thread..but all UI update should be done on the main thread..so that might be the reason you don't see the activity animating

Resources