3
votes

I tried to create a SplashView which display the Default.png in the background and a UIProgressBar in front. But the splash screen is not being updated...

Inside my view controller I load first the splash view with a parameter how many steps my initialisation has and then I start a second thread via NSTimer and after each initialisation step I tell the SplashView to display the new progress value.

All looks good in theory, but when running this app the progress bar is not being updated (the method of the splash screen receives the values, I can see it in the logs). I also tried to add usleep(10000); in between to give the view updates a bit time and also instead of using the progress bar I drew directly on the view and called [self setNeedsDisplay]; but all didn't work :/

What am I doing wrong?

Thanks for your help!

Tom

Here is some code:

SPLASHSCREEN:
- (id)initWithFrame:(CGRect)frame withStepCount:(int)stepCount {
    if (self = [super initWithFrame:frame]) {
        // Initialization code

        background = [[UIImageView alloc] initWithFrame: [self bounds]];
        [background setImage: [UIImage imageWithContentsOfFile: [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], @"Default.png"]]];
        [self addSubview: background];

        progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleBar];
        [progressView setFrame:CGRectMake(60.0f, 222.0f, 200.0f, 20.0f)];
        [progressView setProgress: 0.0f];

        stepValue = 1.0f / (float)stepCount;

        [self addSubview:progressView];
    }
    return self;
}

- (void)tick {
    value += stepValue;
    [progressView setProgress: value];
}



VIEWCONTROLLER:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {

        splashView = [[SplashView alloc] initWithFrame: CGRectMake(0.0f, 0.0f, 320.0f, 480.0f) withStepCount:9];
        [self setView: splashView];

        NSTimer* delayTimer;
        delayTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(finishInitialization) userInfo:nil repeats:NO];
    }
    return self;
}

- (void)finishInitialization {
    // do some stuff, like allocation, opening a db, creating views, heavy stuff...
    [splashView tick]; // this should update the progress bar...

    // do some stuff, like allocation, opening a db, creating views, heavy stuff...
    [splashView tick]; // this should update the progress bar...

    // init done... set the right view and release the SplashView
}
4

4 Answers

5
votes

As mentioned in another answer, for some finite amount of time, as your app is being launched, Default.png is displayed and you have no control over it. However, if in your AppDelegate, you create a new view that displays the same Default.png, you can create a seamless transition from the original Default.png to a view that you can add a progress bar to.

Now, presumably, you have created a view or similar and you are updating a progress bar every so often in order to give the user some feedback. The challenge here is that your view is only drawn when it gets called to do a drawRect. If, however, you go from AppDelegate to some initialization code to a viewcontroller's viewDidLoad, without the run loop getting a chance to figure out which views need to have drawRect called on, then your view will never display its status bar.

Therefore in order to accomplish what you want, you have to either make sure that drawRect gets called, such as by pushing off a lot of your initialization code into other threads or timer tasks, or you can force the drawing by calling drawRect yourself, after setting up contexts and such.

If you go with the background tasks, then make sure your initialization code is thread-safe.

3
votes

Default.png is just a graphic, a static image shown while the application is launching. If you want to show further progress, you'll have to show everything at the applicationDidLaunch phase. Show your modal "Splash Screen" there first (Create a view controller, add its view as a subview of your main window) and dismiss it when you are done whatever additional loading you needed to do.

1
votes

Also, you need to do update your progress bar in a seperate thread. Updating your GUI in the same thread where a lot of business is going on is (in my opinion, but I could be wrong) a bad idea.

1
votes

The main thread is, as far as I know, the only one that can safely do GUI things, and its event loop (that is, the main application thread's) is the one that does the actual displaying after you've called -setNeedsDisplay. Spawn a new thread to do your loading, and update the progress on the main thread.