Reputation: 697
In this project I use Foursquare API
to get some venues and one photo from each venue. When I have those data, I'm reloading the TableView
to display all the info.
First I collect the venues and secondly for each venue, I m taking a photo from foursquare API
.
I m using the RestKit
library for this project and I'm calling this method n times (one time for each venue). When it's finishing I want to display all those photos I have taken to my table view.
- (void)requestVenuePhoto:(Venue *)thisVenue{
//...
//...
[[RKObjectManager sharedManager] getObjectsAtPath:objectPath parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
[self.photos addObjectsFromArray:mappingResult.array];
//[self.tableView reloadData];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(@"What do you mean by 'there is no photos?': %@", error);
dispatch_group_leave(resolveVenuePhotos);
}];
}
The problem is that I can't use the dispatch_group_leave because is'not called one time only. Is there any way to do this nicely?
Update, now I'm using a counter to solve the problem:
[[RKObjectManager sharedManager] getObjectsAtPath:objectPath parameters:queryParams success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
[self.photos addObjectsFromArray:mappingResult.array];
venuesPhotoCounter++;
if (venuesPhotoCounter == _venues.count)
{
[self.tableView reloadData];
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(@"What do you mean by 'there is no photos?': %@", error);
}];
Upvotes: 1
Views: 299
Reputation: 308
Apple has uploaded a project called "LazyTableImages" available here . So I wrapped it up and made some paradigms which should probably fit your use-case.
- (void)startDownload
{
NSURLRequest *request = [NSURLRequest requestWithURL:self.venue.imageURL];
// create an session data task to obtain and download the app icon
_sessionTask = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// in case we want to know the response status code
NSInteger HTTPStatusCode = [(NSHTTPURLResponse *)response statusCode];
if (error != nil)
{
if ([error code] == NSURLErrorAppTransportSecurityRequiresSecureConnection)
{
// if you get error NSURLErrorAppTransportSecurityRequiresSecureConnection (-1022),
// then your Info.plist has not been properly configured to match the target server.
//
abort();
}
}
[[NSOperationQueue mainQueue] addOperationWithBlock: ^{
// Set appIcon and clear temporary data/image
UIImage *image = [[UIImage alloc] initWithData:data];
if (HTTPStatusCode == 200) {
if (image.size.width != kAppIconSize || image.size.height != kAppIconSize)
{
CGSize itemSize = CGSizeMake(kAppIconSize, kAppIconSize);
UIGraphicsBeginImageContextWithOptions(itemSize, NO, 0.0f);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
self.venue.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
else
{
self.venue.image = image;
}
}
else {// If anything goes wrong we should use a placeholder image
self.venue.image = [UIImage imageNamed:@"Placeholder.png"];
}
// call our completion handler to tell our client that our icon is ready for display
if (self.completionHandler != nil)
{
self.completionHandler();
}
}];
}];
[self.sessionTask resume];
}
Upvotes: 1
Reputation: 17710
This is not a direct answer to your question, but I think it will resolve your issue.
You should not load all the pictures and only then reload the table. You should:
load the list of items, and in your completion handler, reload the table (make sure the reload happens on the main thread if you completion handler runs on a background thread).
then, in the tableView:cellForItemAtIndexPath:
method of your tableview datasource, start loading the image for the requested item. Also make a note of the current indexPath (often just the row), for instance in the tag
of the cell, or in a custom cell property. In the completion handler for that request, decode the image (in the background), then assign it to your cell (on the main thread), after having check that the cell is still for the same indexPath (i.e. the cell has not been reused).
Example using standard NSURLSession
(based on a single section, and data being in an _items instance variable):
- tableView:(UITableView *)tableView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyItem *item = _items[indexPath.row];
MyCustomCell *cell = (MyCustomCell *)[tableView dequeueReusableCellWithIdentifier:@"whatever"];
// configure the rest of the cell: labels, etc.
cell.tag = indexPath.row;
[[[NSURLSession sharedSession] dataTaskWithURL:item.url
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
// Remember this runs on a background thread
if (cell.tag == indexPath.row && !error && data)
{
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(),^
{
cell.myImageView.image = image;
});
}
}] resume];
}
Upvotes: 1