解析 PFFile 下载顺序 iOS
Parse PFFile download order iOS
我将 5 个 PFFiles 存储在一个数组中,并使用 getDataInBackgroundWithBlock
从 Parse 下载这些文件。
问题是它们在 table 视图单元格中出现的顺序每次都不同,大概是因为文件大小不同,文件下载速度不同。
for (PFFile *imageFile in self.imageFiles) {
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
[self.avatars addObject:avatar];
cell.userImageView.image = self.avatars[indexPath.row];
}
}];
}
self.imageFiles
数组的顺序正确。
如何确保下载的图像以与 self.imageFiles
相同的顺序添加到 self.avatars
数组?
问题分为两部分:(1)明确地,如何维护异步操作结果的顺序,(2)隐含地使用cell
,如何正确处理异步请求以支持一个表格视图。
第一个问题的答案更简单:将请求的结果与请求的参数关联起来。
// change avatars to hold dictionaries associating PFFiles with images
@property(nonatomic,strong) NSMutableArray *avatars;
// initialize it like this
for (PFFile *imageFile in self.imageFiles) {
[avatars addObject:[@{@"pfFile":imageFile} mutableCopy]];
}
// now lets factor an avatar fetch into its own method
- (void)avatarForIndexPath:(NSIndexPath *)indexPath completion:^(UIImage *, NSError *)completion {
// if we fetched already, just return it via the completion block
UIImage *existingImage = self.avatars[indexPath.row][@"image"];
if (existingImage) return completion(existingImage, nil);
PFFile *pfFile = self.avatars[indexPath.row][@"pfFile"];
[pfFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
self.avatars[indexPath.row][@"image"] = avatar;
completion(avatar, nil);
} else {
completion(nil, error);
}
}];
}
第 (1) 部分没问题。对于第 2 部分,您的 cellForRowAtIndexPath
代码必须识别重复使用的单元格。当异步图像获取发生时,您正在处理的单元格可能已经滚开。通过不引用完成块中的单元格(仅 indexPath
)来解决此问题。
// somewhere in cellForRowAtIndexPath
// we're ready to setup the cell's image view
UIImage *existingImage = self.avatars[indexPath.row][@"image"];
if (existingImage) {
cell.userImageView.image = existingImage;
} else {
cell.userImageView.image = // you can put a placeholder image here while we do the fetch
[self avatarForIndexPath:indexPath completion:^(UIImage *image, NSError *error) {
// here's the trick that is often missed, don't refer to the cell, instead:
if (!error) {
[tableView reloadRowsAtIndexPaths:@[indexPath]];
}
}];
}
重新加载完成块中的行将导致再次调用 cellForRowAtIndexPath
,除了在后续调用中,我们将拥有一个现有图像并且单元格将立即配置。
虽然 danh 的回答已经回答了我的问题,但我确实在发布问题后不久设法解决了它。我正在捕获每个 imageFile 的索引并确保它们按顺序添加到 self.avatars
数组。
for (PFFile *imageFile in self.imageFiles) {
NSInteger index = [self.imageFiles indexOfObject:imageFile];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
self.avatars[index] = avatar;
[self.tableView reloadData];
}
}];
}
然后 cell.userImageView.image = self.avatars[indexPath.row];
在 cellForRowAtIndexPath:
我将 5 个 PFFiles 存储在一个数组中,并使用 getDataInBackgroundWithBlock
从 Parse 下载这些文件。
问题是它们在 table 视图单元格中出现的顺序每次都不同,大概是因为文件大小不同,文件下载速度不同。
for (PFFile *imageFile in self.imageFiles) {
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
[self.avatars addObject:avatar];
cell.userImageView.image = self.avatars[indexPath.row];
}
}];
}
self.imageFiles
数组的顺序正确。
如何确保下载的图像以与 self.imageFiles
相同的顺序添加到 self.avatars
数组?
问题分为两部分:(1)明确地,如何维护异步操作结果的顺序,(2)隐含地使用cell
,如何正确处理异步请求以支持一个表格视图。
第一个问题的答案更简单:将请求的结果与请求的参数关联起来。
// change avatars to hold dictionaries associating PFFiles with images
@property(nonatomic,strong) NSMutableArray *avatars;
// initialize it like this
for (PFFile *imageFile in self.imageFiles) {
[avatars addObject:[@{@"pfFile":imageFile} mutableCopy]];
}
// now lets factor an avatar fetch into its own method
- (void)avatarForIndexPath:(NSIndexPath *)indexPath completion:^(UIImage *, NSError *)completion {
// if we fetched already, just return it via the completion block
UIImage *existingImage = self.avatars[indexPath.row][@"image"];
if (existingImage) return completion(existingImage, nil);
PFFile *pfFile = self.avatars[indexPath.row][@"pfFile"];
[pfFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
self.avatars[indexPath.row][@"image"] = avatar;
completion(avatar, nil);
} else {
completion(nil, error);
}
}];
}
第 (1) 部分没问题。对于第 2 部分,您的 cellForRowAtIndexPath
代码必须识别重复使用的单元格。当异步图像获取发生时,您正在处理的单元格可能已经滚开。通过不引用完成块中的单元格(仅 indexPath
)来解决此问题。
// somewhere in cellForRowAtIndexPath
// we're ready to setup the cell's image view
UIImage *existingImage = self.avatars[indexPath.row][@"image"];
if (existingImage) {
cell.userImageView.image = existingImage;
} else {
cell.userImageView.image = // you can put a placeholder image here while we do the fetch
[self avatarForIndexPath:indexPath completion:^(UIImage *image, NSError *error) {
// here's the trick that is often missed, don't refer to the cell, instead:
if (!error) {
[tableView reloadRowsAtIndexPaths:@[indexPath]];
}
}];
}
重新加载完成块中的行将导致再次调用 cellForRowAtIndexPath
,除了在后续调用中,我们将拥有一个现有图像并且单元格将立即配置。
虽然 danh 的回答已经回答了我的问题,但我确实在发布问题后不久设法解决了它。我正在捕获每个 imageFile 的索引并确保它们按顺序添加到 self.avatars
数组。
for (PFFile *imageFile in self.imageFiles) {
NSInteger index = [self.imageFiles indexOfObject:imageFile];
[imageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
if (!error) {
UIImage *avatar = [UIImage imageWithData:imageData];
self.avatars[index] = avatar;
[self.tableView reloadData];
}
}];
}
然后 cell.userImageView.image = self.avatars[indexPath.row];
在 cellForRowAtIndexPath: