0

I have a UIBarButtonItem called "Refresh Data". When user clicks it, the app should refresh its data. What happens on that button click is web services are launched and xml data is brought and they are of the order 30000-40000 records. So to prevent UI from hanging i wrote a background thread and did that loading there.

- (void)refreshDataAction
{
NSLog(@"Refresh Data");
//Put up an alert box indicating user to wait while data is loading.

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Data is Loading"
                                                        message:@"Please wait while data is being refreshed."
                                                       delegate:self
                                              cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil, nil];
alert.tag = 10;
[alert show];
 }

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSLog(@"hit in clickedbuttonatindex alertview at 259");
self.refreshActIndicator.hidden = NO;
[self.refreshActIndicator startAnimating];
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                         (unsigned long)NULL), ^(void) {
    [self getAllCustomerValues];

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror])
    {
        NSLog(@"209 Failed to save second MOC");
    }
    else
    {
        //NSLog(@"saved success");
    }
 });
}

Like you can see alert view shows up. I click OK and the box is still there its "blackened" and its hanging there on the screen. And then 7-8 seconds later the box disappears and the animation starts for activity indicator. My goal is to get something like this. 1.User clicks on the refresh data button. 2.Alert view shows up. 3.User clicks OK. 4.Alert box disappears and immediately the background thread starts working and i see the activity indicator working/animating.

User will know to wait while the activity indicator is animating. So how do i get the app to start animating immediately after user clicks "OK" and the alertview OK not be blackened. Am i clear?If you need more information, please ask. Thanks

EDIT: This screen has a background thread working on it when it loads the first time for that day. There is a screen before this. On it i have continue button and clicking that launches background thread which does exactly the same thing like this one over here.I dont kill it or anything.

EDIT 2:

 - (void)refreshDataAction
   {
NSLog(@"Refresh Data");
self.txtCustomerSearch.text =@"";
[self cleanUPPreviousLabels];

//Put up an alert box indicating user to wait while data is loading.

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Data is Loading"
                                                message:@"Please wait while data is being refreshed."
                                               delegate:self
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil, nil];
//alert.tag = 10;
//[alert show];
[alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:FALSE];

NSLog(@"hit in willpresenet alertview at 221");
    self.refreshActIndicator.hidden = NO;
    [self.refreshActIndicator startAnimating];
NSLog(@"Dispatching");

//Disable the view and all the other controls
self.txtCustomerSearch.userInteractionEnabled =NO;
self.txtCustomerSearch.enabled =NO;
self.btnSearch.enabled =NO;
self.btnSearch.userInteractionEnabled = NO;
self.scrollView.userInteractionEnabled = NO;
//self.view.userInteractionEnabled =NO;
self.chkButton.enabled = NO;


[self deletePreviousValues]; 

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    NSLog(@"Getting customer values");
    [self getAllCustomerValues];
    NSLog(@"Got customer values");

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror])
    {
        NSLog(@"209 Failed to save second MOC");
    }
    else
    {
        //NSLog(@"saved success");
    }


    self.txtCustomerSearch.userInteractionEnabled = YES;
    self.txtCustomerSearch.enabled =YES;
    self.btnSearch.enabled =YES;
    self.btnSearch.userInteractionEnabled = YES;
    self.scrollView.userInteractionEnabled = YES;
    self.view.userInteractionEnabled =YES;
    self.chkButton.enabled = YES;

    [self.refreshActIndicator stopAnimating];

    NSLog(@"Saved");


});
NSLog(@"Dispatched");

}

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
RookieAppler
  • 1,517
  • 5
  • 22
  • 58

5 Answers5

0

Try changing

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                     (unsigned long)NULL), ^(void) {

to the background priority

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,
                                     (unsigned long)NULL), ^(void) {
Ric
  • 8,615
  • 3
  • 17
  • 21
  • @Ric.No. Changed it, but it is still like that.Alert box blackened and 7-8 seconds later it disappears and then my animation for activity indicator starts. Also i edited some information in my original post. Please check it out. – RookieAppler Feb 21 '13 at 19:31
0

Use alertView:didDismissWithButtonIndex: instead of alertView:clickedButtonAtIndex:. This way the alert is already dismissed when your code is called.

Question - why bother with the alert view? The user knows they tapped a "refresh" icon. You already show an activity indicator, and the alert doesn't give the user a chance to cancel the refresh.

Update: The call to dispatch_async seems a bit off. Try this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [self getAllCustomerValues];

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror]) {
        NSLog(@"209 Failed to save second MOC");
    } else {
        //NSLog(@"saved success");
    }
});
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Well, when i did not have alert view then on clicking the refresh button it still took 7-8 seconds to show up the activity indicator. So the screen froze for 7-8 seconds with nothing happening. No indicator.Nothing. I dont know why it is taking that time.But now i introduced the alert view to atleast tell them that something is happening. – RookieAppler Feb 21 '13 at 19:44
  • What you describe indicates the data loading (`getAllCustomerValues`) is really running on the main thread. But that shouldn't be the case since you are running that code on a background queue. See my updated answer. – rmaddy Feb 21 '13 at 19:48
  • I changed like you suggested. dismissWithButtonIndex and change in dispatch_async. But its like that only. I click refresh data. Alert view opens. Click Ok to dismiss. Alert box is gone. 7-8 second delay (nothing happens) and then the indicator starts revolving/animating. – RookieAppler Feb 21 '13 at 19:58
  • This makes no sense. See Ric's answer for adding some logging. You should see the "Dispatched" log before any of the logs inside the call to `dispatch_async`. – rmaddy Feb 21 '13 at 20:02
0

It might help to add some NSLogs to see where it is hanging and stop the activity indicator to see if the entire hang happens because of the dispatch.

 NSLog(@"hit in clickedbuttonatindex alertview at 259");
 self.refreshActIndicator.hidden = NO;
 [self.refreshActIndicator startAnimating];
 NSLog(@"Dispatching");
 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
                                     (unsigned long)NULL), ^(void) {
    NSLog(@"Getting customer values");
    [self getAllCustomerValues];
    NSLog(@"Got customer values");

    NSError *nwerror = nil;
    if (![self.secondMOC save:&nwerror])
    {
        NSLog(@"209 Failed to save second MOC");
    }
    else
    {
        //NSLog(@"saved success");
    }
    [self.refreshActIndicator stopAnimating];
    NSLog(@"Saved");
 });
 NSLog(@"Dispatched");

What is happening is that the main (UI) queue is being stopped for some reason, which causes things to appear to hang. Because it is stopped, things like refresh activity indicators will not animate even if you have told them to start animating. And remember that you cannot update the UI in async so secondMOC should not be used to cause any tables to reload data and [self getAllCustomerValues]; should not do any UI stuff.

Ric
  • 8,615
  • 3
  • 17
  • 21
  • So on simulator the output is :hit in clickedbuttonatindex alertview at 259.Dispatching.Getting customer values.Dispatched.Got customer values.Saved. Also on simulator there is no delay (no 7-8 second wait)at all. On the device it is taking time. – RookieAppler Feb 21 '13 at 20:08
  • When you debug on the device - "Run on iOS device" - what does the output look like? – Ric Feb 21 '13 at 20:12
  • When i connect my device to mac, in xcode i get "Phani's iPad,iPad 6.1 Simulator,iPhone 6.1 Simulator". I choose "phani's ipad" and i run, then i dont see any output in my Xcode. What are you saying? Can you explain a little.Thanks – RookieAppler Feb 21 '13 at 20:21
  • Sorry, try 'Debug on iOS device' There should be output. – Ric Feb 21 '13 at 20:28
  • I am using ios6.1. I dont see the option "Debug on iOS devic".Where is that? – RookieAppler Feb 21 '13 at 20:32
  • Wait, running it should show output. Like this: http://stackoverflow.com/questions/3377309/xcode-4-how-do-you-view-the-console – Ric Feb 21 '13 at 20:37
  • I have been trying to follow those steps but they wont lead me to correct output. First when i connect my device and run it says "Could not launch failed to get the task for process 253". Then i changed my code signing to iPhone developer and ran, but it says some other error. – RookieAppler Feb 21 '13 at 21:33
  • I think you need to figure out why that happens before trying to fix the hanging. – Ric Feb 21 '13 at 23:06
  • The culprit here is the [self deletePreviousValues]; It is taking the 13 second delay. How can i prevent that? – RookieAppler Feb 22 '13 at 02:17
  • I am not sure what deletePreviousValues is. I can only guess that it should be the first line inside the dispatch_async. – Ric Feb 22 '13 at 05:30
0

We using MBProgressHUD (https://github.com/jdg/MBProgressHUD) which makes this kind of UI notification easy. Generally you supply a method to be called that can check an atomic BOOL value. Set the BOOL when you put up the HUD, when the background process is complete clear the BOOL and the HUD will automatically go away.

ahwulf
  • 2,584
  • 15
  • 29
0

You say this is all happening on a background thread - make sure you load the UIAlertView on the main thread:

//[alert show];
[alert performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:FALSE];

This will probably fix the problem. Your async call is actually happening on the current thread in the run loop (since it's a background thread), blocking the alert view you're attempting to display (in the same run loop).

escrafford
  • 2,373
  • 16
  • 19
  • I edited my code like you said. It didnt work. I have posted the refreshDataAction revised method there. Take a look and tell me if you find anything wrong. Thanks – RookieAppler Feb 22 '13 at 00:08