如果您知道它被调用了 n 次,请等待异步任务完成
Wait for async task to finish if you know that it's called n times
在这个项目中,我使用 Foursquare API
获取一些场地和每个场地的一张照片。获得这些数据后,我将重新加载 TableView
以显示所有信息。
首先我收集场地,其次为每个场地,我从 foursquare API
拍照。
我正在为这个项目使用 RestKit
库,我调用了这个方法 n 次(每个地点一次)。完成后,我想在 table 视图中显示我拍摄的所有照片。
- (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);
}];
}
问题是我不能使用 dispatch_group_leave 因为它不只被调用一次。
有什么办法可以很好地做到这一点吗?
更新,现在我用计数器来解决问题:
[[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);
}];
这不是您问题的直接答案,但我认为它会解决您的问题。
您不应该加载所有图片,然后才重新加载 table。你应该:
加载项目列表,并在您的完成处理程序中重新加载 table(如果您的完成处理程序在后台线程上运行,请确保重新加载发生在主线程上)。
然后,在 table 视图数据源的 tableView:cellForItemAtIndexPath:
方法中,开始加载所请求项目的图像。还要记下当前的 indexPath(通常只是行),例如在单元格的 tag
中,或在自定义单元格中 属性。在该请求的完成处理程序中,解码图像(在后台),然后将它分配给您的单元格(在主线程上),在检查单元格是否仍然用于相同的 indexPath 之后(即单元格未被重用) ).
使用标准 NSURLSession
的示例(基于单个部分,数据位于 _items 实例变量中):
- 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];
}
Apple 已上传名为 "LazyTableImages" 的项目可用 here
.所以我把它包装起来并制作了一些可能适合您的用例的范例。
- (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];
}
在这个项目中,我使用 Foursquare API
获取一些场地和每个场地的一张照片。获得这些数据后,我将重新加载 TableView
以显示所有信息。
首先我收集场地,其次为每个场地,我从 foursquare API
拍照。
我正在为这个项目使用 RestKit
库,我调用了这个方法 n 次(每个地点一次)。完成后,我想在 table 视图中显示我拍摄的所有照片。
- (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);
}];
}
问题是我不能使用 dispatch_group_leave 因为它不只被调用一次。 有什么办法可以很好地做到这一点吗?
更新,现在我用计数器来解决问题:
[[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);
}];
这不是您问题的直接答案,但我认为它会解决您的问题。
您不应该加载所有图片,然后才重新加载 table。你应该:
加载项目列表,并在您的完成处理程序中重新加载 table(如果您的完成处理程序在后台线程上运行,请确保重新加载发生在主线程上)。
然后,在 table 视图数据源的
tableView:cellForItemAtIndexPath:
方法中,开始加载所请求项目的图像。还要记下当前的 indexPath(通常只是行),例如在单元格的tag
中,或在自定义单元格中 属性。在该请求的完成处理程序中,解码图像(在后台),然后将它分配给您的单元格(在主线程上),在检查单元格是否仍然用于相同的 indexPath 之后(即单元格未被重用) ).
使用标准 NSURLSession
的示例(基于单个部分,数据位于 _items 实例变量中):
- 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];
}
Apple 已上传名为 "LazyTableImages" 的项目可用 here .所以我把它包装起来并制作了一些可能适合您的用例的范例。
- (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];
}