You are mixing and matching synchronous and asynchronous methodologies. There are two possible approaches.
Make the return of your
photoCount
method asynchronous by passing a completion block.- (void)PhotoCount:(void(^)(NSUInteger))completionBlock { ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; __block NSInteger result = 0; void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop){ if(group != nil) { if([[group valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) { result += [group numberOfAssets]; } } else { // nil group signals we're done with enumeration if (completionBlock) { completionBlock(result); } } }; [library enumerateGroupsWithTypes: ALAssetsGroupSavedPhotos usingBlock:assetGroupEnumerator failureBlock:^(NSError *error) {NSLog(@"Problems");} ]; }
or
Block the current thread until the operation is complete. This is not usually a good idea in an interactive user app. If you're doing this, you should rethink how this part of the application is structured:
- (NSUInteger)PhotoCount { ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init]; __block NSInteger result = 0; dispatch_semaphore_t blockSemaphore = dispatch_semaphore_create(0); void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop){ if(group != nil) { if([[group valueForProperty:ALAssetPropertyType] isEqualToString:ALAssetTypePhoto]) { result += [group numberOfAssets]; } } else { // nil group signals we're done with enumeration dispatch_semaphore_signal(blockSemaphore); } }; [library enumerateGroupsWithTypes: ALAssetsGroupSavedPhotos usingBlock:assetGroupEnumerator failureBlock:^(NSError *error) {NSLog(@"Problems");} ]; dispatch_semaphore_wait(blockSemaphore, DISPATCH_TIME_FOREVER); NSLog(@"%u", result); return result; }
You were also using the resultBlock
variable in a way I didn't understand at all, so I omitted it from my answer.
Just to be clear, I wouldn't go with option 2. It will cause a noticeable delay in the responsiveness of your app, especially if you're calling this on the main thread, and especially if the user has a large asset library.
A great benefit of programming with blocks is that you can defer doing work until you have all the information you need to do the work. Presumably you're going to change some UI element in response to whatever this number turns out to be. Put the code to do that in the completion block that you pass to the method above. Or better yet, factor that code out into a method that you then invoke from the block.
To continue with how you'd use the asynchronous approach, in the collection view controller, you'd want a method that precomputes this number, perhaps when the controller loads:
@property (assign) NSUInteger numberOfPhotos;
- (void)beginLoadingCollectionInformation {
__weak <<<Type of Self>>> *__self = self;
[self PhotoCount:^(NSUInteger photoCount) {
__self.numberOfPhotos = photoCount;
[__self.collectionView reloadData];
}];
}
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section {
return self.numberOfPhotos;
}
If you can manage to get the photo count information into your class before the view appears, that might be ideal. Otherwise, you'll have to insert the content afterwards. It's not ideal, and it's not exactly what I'd do in this case. But this question is starting to stray off into data sources, delegation patterns, and app architecture – well beyond the question of synchronous and asyncronous return values on methods.
Hope that helps.
One last note answering the last question I think you're asking. Change your data reloading call so that it happens within the completion block:
//Photo collection info
- (void)beginLoadingPhotoInfo {
NSLog(@"loaded beginloadingphotoinfo");
__weak CoverViewController *__self = self;
[self PhotoCount:^(NSUInteger photoCount) {
__self.numberOfPhotos = photoCount;
[__self.CoverView reloadData];
}];
}