Search code examples
iosmultithreadingcocos2d-iphone

Cocos2D updating UI with threads


I have a "Loading Screen" with a "Loading bar". This loading screen gets images from my server and loads them to CCSprites, so i will be using them in other screens. While the loading screen is downloading images and creating the CCSprites, i want my Loading Bar to update it's UI. LoadingBar is a CCNode subclass.

I know I should use threading to update UI, but the problem is that (as far as I know) Cocos2D nodes are not thread safe so when i use the code below, it does update the UI but then the sprites does not load:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, kNilOptions), ^{
    // go do something asynchronous...
    dispatch_async(dispatch_get_main_queue(), ^{
        // update UI
    });
});

I have tried many things, but i can't solve this problem. Please, help :)

Here is the code that makes my LoadingBar update:

-(void)makeStep{
int actualPersentage = 100 * (numSteps - numStepsToGo + 1) / numSteps;
int objectAtIndex = MAX(0, numSteps - numStepsToGo);
persentageLabel.string = [NSString stringWithFormat:@"%i%% %@", actualPersentage, [steps objectAtIndex:objectAtIndex]];
[frontground setTextureRect:CGRectMake(0, 0, frontground.textureRect.size.width + (200/numSteps), 40)];
frontground.position = ccp(initialPosition + (frontground.contentSize.width/2) - (background.contentSize.width/2), frontground.position.y);
numStepsToGo --;

if(numStepsToGo == 0){
    [frontground setTextureRect:CGRectMake(0, 0, 200, 40)];
    persentageLabel.string = [NSString stringWithFormat:@"All loaded correctly!"];
}
}

Basically what I have is a background gray rectangle as background and a green one (frontground) that "fills" the background each time makeStep is called.

Here is the code example where i parse a JSON and where my LoadingBar should update:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, kNilOptions), ^{
...
CCSprite *ssButtonStart = [self getSpriteFromUrl:[startScreenData objectForKey:@"SSimg_button_start"]];
CCSprite *ssButtonExit = [self getSpriteFromUrl:[startScreenData objectForKey:@"SSimg_button_exit"]];
CCSprite *ssBackground = [self getSpriteFromUrl:[startScreenData objectForKey:@"SSimg_background"]];
self.gameProperties.slBackground = ssBackground;
self.gameProperties.slButtonExit = ssButtonExit;
self.gameProperties.slButtonStart = ssButtonStart;
NSLog(@"Start Screen Loading done!");
dispatch_async(dispatch_get_main_queue(), ^{
    [self updateStep];
});
...
dispatch_async(dispatch_get_main_queue(), ^{
    [self updateStep];
});
//Etc.

Code where i get the image and convert to CCSprite:

-(CCSprite*)getSpriteFromUrl:(NSString*)stringUrl{
__block NSData *imageData;
__block CCSprite *sprite;
    imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString:stringUrl]];
        //NSLog(@"string imageData: %@", imageData);
        UIImageView *imView = [[UIImageView alloc] initWithImage:[UIImage imageWithData: imageData]];
    dispatch_async(dispatch_get_main_queue(), ^{
        CCTexture2D *texture = [[CCTexture2D alloc] initWithCGImage:imView.image.CGImage resolutionType:kCCResolutionUnknown];
        if(texture != nil){
            sprite = [[CCSprite alloc] init];
            sprite = [[CCSprite alloc] initWithTexture:texture];
        }else{
            [self showError:@"Some textures didn't load correctly. Please try again!"];
        }
        // update UI
    });
return sprite;
}

I am using iOS 7 and Cocos2D 2.0


Solution

  • You should update the bar on the main thread. Instead move the downloading of images to a background thread. NSData or NSURLRequest even has async methods to do so. Then when you have completed downloading the images create the CCSprites on the main thread as a last step. Sprites have to be created on the main thread anyway.

    This answer from LearnCocos2D (see code in question) plus:

    Solved! I made an sleep after getting every sprite in getSpriteFromUrl. The code is [NSThread sleepForTimeInterval:0.2];. Hope it helps anyone else!

    From me, solved my question. Thanks!