从服务器下载图像以显示在 CollectionView 上
Posted
技术标签:
【中文标题】从服务器下载图像以显示在 CollectionView 上【英文标题】:Download Images from a server to display on the CollectionView 【发布时间】:2015-10-28 17:31:08 【问题描述】:我正在开发一个用户可以出售/购买的产品应用程序。此应用程序基于集合视图。集合视图具有集合单元格,其中显示产品图像缩略图。
以下代码从服务器获取产品图像,并等待下载所有图像,然后将它们显示在单元格中。以下代码有效,但用户等待 10-20 秒才能查看所有产品。有没有更好的处理方式?
- (void)viewDidLoad
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^(void)
[self loadFromURL];
dispatch_async(dispatch_get_main_queue(), ^
);
);
-(void)loadFromURL
NSURL *url = [NSURL URLWithString:@"http://myURL/productAll.php"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
pElements= (NSMutableArray *)responseObject;
[collectionView reloadData];
failure:^(AFHTTPRequestOperation *operation, NSError *error)
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Product" message:[error localizedDescription]delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alertView show];
];
[operation start];
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
return pElements.count;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
ProductCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
cell.backgroundColor=[UIColor whiteColor];
NSString *arrayResult = [[pElements objectAtIndex:indexPath.row] objectForKey:@"image"];
NSData *data = [[NSData alloc] initWithBase64EncodedString:arrayResult options:NSDataBase64DecodingIgnoreUnknownCharacters];
cell.productImage.image = [[UIImage alloc] initWithData:data];
cell.productImage.layer.cornerRadius = 10;
cell.productImage.clipsToBounds = YES;
return cell;
【问题讨论】:
如果你正在寻找代码审查,你应该在codereview.stackexchange.com询问 实际上,我已经修改了我的问题,我正在尝试找出如何减少用户等待在集合视图上查看产品的时间。 @rmaddy:这看起来不像是代码审查,而是询问如何优化“不完美”代码的不同意见。 @erolyeniaras 这个问题在我发表评论后改变了。 @texas Nice quistain 【参考方案1】:您有来自服务器的响应,其中所有图像的图像数据都在响应中以 base-64 编码。这意味着响应可能非常大,并且在下载所有内容之前不会显示给用户。
相反,您可以考虑重构您的服务器代码,使其不包含 base-64 格式的图像数据,而是仅包含一个 URL(或某些标识符),以便以后检索图像。您的响应应该小得多,并且应该能够更快地得到处理。
然后,当调用cellForItemAtIndexPath
时,您不会从原始响应中提取图像数据,而是懒惰地(并且异步地)请求单元格的图像。 AFNetworking 在UIImageView+AFNetworking
中提供了一个不错的UIImageView
类别,可以从网络源中异步检索图像。 (在进行异步图像检索时,使用这个类别可以让您摆脱许多微妙的问题。)
顺便说一下,如果您的图片大小不一,您可能希望在原始请求中包含图片的尺寸,以便可以预先调整单元格及其图片视图的大小,而不是将它们的大小调整为检索图像。
--
几个观察:
您不需要将[self loadFromURL]
分派到后台队列,因为这已经是异步的。我可能会使用请求的GET
您不能只将responseObject
转换为NSMutableArray
,因为它可能是不可变的。如果你真的需要它是可变的,你真的应该使用NSArray
或使用mutableCopy
。
您正在cellForItemAtIndexPath
中进行一些单元配置。大部分(剪辑、背景颜色等)都可以在 IB 中完成,所以我会在那里完成,而不是通过编程方式完成。您可能需要以编程方式做的唯一事情是圆角(即使这样,我也可能会使用 IBDesignable
子类,尽管这超出了这个问题的范围)。
因此,假设 (a) 您的数组有一个名为 imageURL
的新属性,它是图像的 URL; (b) 单元格具有固定大小的图像视图,您可以执行以下操作:
@interface ViewController ()
@property (nonatomic, strong) AFHTTPRequestOperationManager *manager;
@property (nonatomic, strong) NSArray *pElements;
@end
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
self.manager = [AFHTTPRequestOperationManager manager];
[self loadFromURL];
-(void)loadFromURL
NSString *urlString = @"http://myURL/productAll.php";
[self.manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject)
self.pElements = responseObject;
[self.collectionView reloadData];
failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error)
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Product" message:[error localizedDescription]delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alertView show];
];
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
return self.pElements.count;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
ProductCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
NSString *imageURL = self.pElements[indexPath.row][@"imageURL"];
[cell.productImage setImageWithURL:[NSURL URLWithString:imageURL]];
cell.productImage.layer.cornerRadius = 10;
return cell;
@end
【讨论】:
非常感谢 Rob,他写了一个非常好的信息丰富的答案。我想知道你能不能说明一点UIIMageView+AFNetworking
?
我的另一个顾虑是我现在首先获取所有产品,然后将它们显示在集合视图上。有没有更好的方法来处理它?例如,如果从服务器下载任何单个图像,则立即显示它。换句话说,不要等待下载所有图像。
@texas 如果从原始响应中删除base-64编码的数据,然后在cellForItemAtIndexPath
中使用UIImageView(AFNetworking)
类别(例如setImageWithURL:
),那么它将首先获取所有产品(应该相当快),然后在图像进入时显示它们,即它不会等待所有图像下载。如果你用谷歌搜索“AFNetworking UIImageView setImageWithURL tutorial”或类似的东西,你应该会得到很好的点击,比如raywenderlich.com/59255/afnetworking-2-0-tutorial
非常感谢 Rob,感谢您的所有帮助和努力。我已接受并赞成您的回答。我希望许多其他 SOF 用户能从中受益
当然可以,但是您只需要在 Web 服务上实现一些 URL,您就可以为 blob 传递一些标识符,它将返回结果图像。例如,您可能在表中具有该行的唯一标识符,并且您将实现一个新的 PHP 来获取该标识并返回图像。我什至会让 PHP 不对 JSON 中的图像进行 base-64 编码,而是返回实际图像。以上是关于从服务器下载图像以显示在 CollectionView 上的主要内容,如果未能解决你的问题,请参考以下文章