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
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!