Question

At several points in my project, I need to perform some synchronous web service calls or read from CoreData. It may take a couple seconds to preform these operations, so I created a LoadingView class that is a subclass of UIView that has a simple message and a UIActivityIndicatorView. For normal UIButtons, I just [myLoadingView setHidden:NO] on the Touch Down event, and [myLoadingView setHidden:YES] on the Touch Up Inside event. This all works exactly how I want.

The problem is I cannot find out how to do this with the Return key on the keyboard. From what I can tell, there is only 1 method that gets called when the user touches the Return key (the UITextFieldDelegate protocol textFieldShouldReturn:), and I need two methods to be able to get my [myLoadingView setHidden:NO] -> [myLoadingView setHidden:YES] technique to work, since Objective-C doesn't update the screen until the very end of the method, as opposed to continuously updating the screen like other languages.

How can I make my Loading screen show up as soon as the user touches the Return key, perform some operations, and they hide the Loading screen once the operations are done?


EDIT:

I've tried looking at using NSNotificationCenter, but I seem to be running into the same problem. For example, if I have a UIButton Touch Up Inside method:

- (void) btnClick:(id) sender
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"Show Loading" object:nil];

    // Do stuff that takes a long time here

    [[NSNotificationCenter defaultCenter] postNotificationName:@"Hide Loading" object:nil];
}

Where in my LoadingView.m I have:

- (id) init
{
    self = [super init];
    if (self)
    {
        // Do normal init stuff here

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showLoading) name:@"Show Loading" object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(hideLoading) name:@"Hide Loading" object:nil];
    }
    return self;
}

- (void) showLoading
{
    // Do stuff to set up the loading screen 

    [self setHidden:NO];
}

- (void) hideLoading
{
    [self setHidden:YES];
}

With it set up like this, when I click the button, I never see the loading screen. When I post the notification, does it execute and change the screen right away? If it does, I must be doing something wrong, maybe in my // Do stuff parts. If it does not, I don't think NSNotificationCenter is going to help me any =(.


EDIT 2:

I set up a quick test project, and I confirmed that the notifications DO NOT update the screen right away. The btnClick code I posted in my last EDIT behaves exactly the same as simply doing

- (void) btnClick:(id) sender
{
    [loadingView setHidden:NO];

    // Do stuff that takes a long time here

    [loadingView setHidden:YES];
}

So that confirms that nothing that relies on NSNotificationCenter is going to help me.

Really, it just looks like there isn't any support for changing the screen during a synchronous operation, which is really disappointing. I'm out of ideas at this point.

Was it helpful?

Solution 3

I ended up using Grand Central Dispatch to handle this. The simplest way is to

- (void) doStuff
{
    // Show wait on start
    [myLoadingView setHidden:NO];

    dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
    dispatch_async(queue, ^{
        // Code to execute
        {
            //... Do my time consuming stuff here ...
            // For testing purposes, I'm using
            int i = 0;
            while (i < 1000000000)
            {
                i++;
            }
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            // Hide Wait Screen on End
            // ... also need to do any other UI stuff here ...
            [myLoadingView setHidden:YES];
        });
    });
}

Unfortunately for me, my actual time consuming stuff has some GUI changes in it (like showing alerts, preforming segues, changing labels, etc.). Instead of rewriting the whole thing to try to pull all the GUI stuff out, I discovered that nesting it all in another dispatch_async, I can still get the waiting screen to show while the operations are performing, and all the other GUI stuff that is done in time consuming stuff will update when it's done (after the waiting screen disappears).

- (void) doStuff
{
    // Show wait on start
    [myLoadingView setHidden:NO];

    dispatch_queue_t queue = dispatch_queue_create("com.myDomain.myApp",null);
    dispatch_async(queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            // Double nesting the dispatches seems to allow me to do UI changes as part of 'Code to execute' below.
            // If I do not double nest like this, GUI behavior in "time consuming stuff" will be erratic
            dispatch_queue_t queue2 = dispatch_queue_create("com.myDomain.myApp",null);
            dispatch_async(queue2, ^{
                dispatch_async(dispatch_get_main_queue(), ^{

                    // Code to execute
                    {
                        //... Do my time consuming stuff here - GUI changes will appear when all the code has finished running ...
                        // For testing purposes, I'm using
                        int i = 0;
                        while (i < 1000000000)
                        {
                            i++;
                        }
                    }

                    // Hide Wait Screen on End
                    [myLoadingView setHidden:YES];
                });
            });
        });
    });
}

Note that if doStuff needs to return a value, you'll probably need to change it up a little to use a "completion block", detailed in the accepted answer here.

OTHER TIPS

You need a callback from whatever processes you're calling to let you know they've completed. When the callback fires, you can hide the loading screen.

You can ofcourse also use didEndOnExit event of textfield, that gets fired when you press the return key.

You can start showing the activity indicator view in this method and when the loading completes, you can use some callback(in case of web service- NSURLConnectionDataDelegate Protocol method: connectionDidFinishLoading) to hide the indicator view.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top