在Objective-C中下载图片集并在表格单元格展示的实现咨询
服务器图片下载并在TableViewCell中展示(Completion Block & Delegate实现)
没问题,我来给你详细讲讲怎么实现从服务器下载图片并在表格单元格里展示,分别用Completion Block和Delegate两种方式,结合你提供的代码片段来调整~
一、Completion Block 实现方式
这种方式的好处是逻辑更紧凑,下载完成后的回调直接写在调用处,不用额外处理代理协议,适合简单场景。
1. 改造你的ImagePlayerView,添加带Completion的下载方法
首先给自定义的ImagePlayerView扩展一个带回调的下载方法,异步完成下载后回到主线程更新UI并通知结果:
@interface ImagePlayerView : UIView // 新增带completion的下载方法 - (void)downloadImagesWithCompletion:(void(^)(BOOL success, NSArray<UIImage *> *images))completion; @end @implementation ImagePlayerView - (void)downloadImagesWithCompletion:(void(^)(BOOL success, NSArray<UIImage *> *images))completion { // 异步在全局队列执行下载,避免阻塞主线程 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSMutableArray *downloadedImages = [NSMutableArray array]; // 这里替换成你实际的服务器图片URL数组 NSArray *imageURLs = @[@"https://example.com/image1.jpg", @"https://example.com/image2.jpg"]; for (NSString *urlStr in imageURLs) { NSURL *url = [NSURL URLWithString:urlStr]; // 注意:实际项目中不要用dataWithContentsOfURL,建议用NSURLSession或第三方库(如SDWebImage) NSData *imageData = [NSData dataWithContentsOfURL:url]; if (imageData) { UIImage *image = [UIImage imageWithData:imageData]; if (image) { [downloadedImages addObject:image]; } } } // 回到主线程更新UI并执行回调 dispatch_async(dispatch_get_main_queue(), ^{ // 把下载好的图片展示到View里(这里是你的自定义展示逻辑,比如轮播) [self displayDownloadedImages:downloadedImages]; // 执行completion回调,通知外部下载结果 if (completion) { BOOL isAllSuccess = downloadedImages.count == imageURLs.count; completion(isAllSuccess, [downloadedImages copy]); } }); }); } // 自定义的图片展示逻辑,根据你的需求实现(比如添加UIImageView到View上) - (void)displayDownloadedImages:(NSArray<UIImage *> *)images { // 先清空旧的图片视图 for (UIView *subview in self.subviews) { if ([subview isKindOfClass:[UIImageView class]]) { [subview removeFromSuperview]; } } // 添加新的图片视图(这里以横向排列为例) CGFloat imgWidth = self.bounds.size.width; for (NSInteger i = 0; i < images.count; i++) { UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(i * imgWidth, 0, imgWidth, self.bounds.size.height)]; imgView.image = images[i]; imgView.contentMode = UIViewContentModeScaleAspectFill; imgView.clipsToBounds = YES; [self addSubview:imgView]; } } @end
2. 在TableView的Cell中调用
在tableView:cellForRowAtIndexPath:方法中,创建并添加ImagePlayerView,然后调用带回调的下载方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"PlayerCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath]; // 处理单元格复用:移除旧的ImagePlayerView,避免重复添加 for (UIView *subview in cell.contentView.subviews) { if ([subview isKindOfClass:[ImagePlayerView class]]) { [subview removeFromSuperview]; } } // 创建ImagePlayerView并添加到Cell ImagePlayerView *imagePlayerView = [[ImagePlayerView alloc] initWithFrame:CGRectMake(0, 0, cell.contentView.bounds.size.width, 150)]; [cell.contentView addSubview:imagePlayerView]; // 调用下载方法,处理回调 [imagePlayerView downloadImagesWithCompletion:^(BOOL success, NSArray<UIImage *> *images) { if (success) { NSLog(@"第%ld行图片全部下载完成", indexPath.row); // 这里可以添加下载完成后的额外逻辑,比如显示提示 } else { NSLog(@"第%ld行部分图片下载失败", indexPath.row); } }]; return cell; }
二、Delegate 实现方式
如果你的下载逻辑需要和多个组件交互,或者希望代码解耦,Delegate方式会更合适。
1. 给ImagePlayerView定义代理协议
首先定义代理协议,然后在下载完成后通过代理通知外部:
// 定义代理协议 @protocol ImagePlayerViewDelegate <NSObject> @optional - (void)imagePlayerView:(ImagePlayerView *)playerView didFinishDownloadingImages:(NSArray<UIImage *> *)images success:(BOOL)success; @end @interface ImagePlayerView : UIView @property (nonatomic, weak) id<ImagePlayerViewDelegate> delegate; // 保留你原来的downloadImages方法 - (void)downloadImages; @end @implementation ImagePlayerView - (void)downloadImages { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSMutableArray *downloadedImages = [NSMutableArray array]; NSArray *imageURLs = @[@"https://example.com/image1.jpg", @"https://example.com/image2.jpg"]; for (NSString *urlStr in imageURLs) { NSURL *url = [NSURL URLWithString:urlStr]; NSData *imageData = [NSData dataWithContentsOfURL:url]; if (imageData) { UIImage *image = [UIImage imageWithData:imageData]; if (image) { [downloadedImages addObject:image]; } } } dispatch_async(dispatch_get_main_queue(), ^{ [self displayDownloadedImages:downloadedImages]; // 通过代理通知外部下载结果 if ([self.delegate respondsToSelector:@selector(imagePlayerView:didFinishDownloadingImages:success:)]) { BOOL isAllSuccess = downloadedImages.count == imageURLs.count; [self.delegate imagePlayerView:self didFinishDownloadingImages:[downloadedImages copy] success:isAllSuccess]; } }); }); } // displayDownloadedImages方法和上面一致,省略 @end
2. 在ViewController中实现代理
让你的ViewController遵守代理协议,然后在Cell中设置代理:
// 遵守代理协议 @interface YourViewController () <UITableViewDataSource, UITableViewDelegate, ImagePlayerViewDelegate> @property (nonatomic, strong) UITableView *tableView; @end @implementation YourViewController // ... 其他初始化代码 ... - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"PlayerCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID forIndexPath:indexPath]; // 处理复用,移除旧的ImagePlayerView for (UIView *subview in cell.contentView.subviews) { if ([subview isKindOfClass:[ImagePlayerView class]]) { [subview removeFromSuperview]; } } ImagePlayerView *imagePlayerView = [[ImagePlayerView alloc] initWithFrame:CGRectMake(0, 0, cell.contentView.bounds.size.width, 150)]; // 设置代理 imagePlayerView.delegate = self; [cell.contentView addSubview:imagePlayerView]; // 调用你原来的下载方法,和你提供的代码逻辑一致 dispatch_async(dispatch_get_main_queue(), ^{ [imagePlayerView downloadImages]; }); return cell; } // 实现代理方法 - (void)imagePlayerView:(ImagePlayerView *)playerView didFinishDownloadingImages:(NSArray<UIImage *> *)images success:(BOOL)success { // 找到当前View所在的Cell的indexPath(可选,根据需求处理) UITableViewCell *cell = (UITableViewCell *)playerView.superview.superview; NSIndexPath *indexPath = [self.tableView indexPathForCell:cell]; if (success) { NSLog(@"第%ld行图片下载完成", indexPath.row); } else { NSLog(@"第%ld行部分图片下载失败", indexPath.row); } } @end
重要注意事项
- 不要用
dataWithContentsOfURL::这个方法是同步的,没有超时控制,实际项目中建议用NSURLSession或者成熟的第三方库(比如SDWebImage、Kingfisher),它们还自带图片缓存,能避免重复下载,提升性能。 - 处理单元格复用:一定要在复用Cell时移除旧的
ImagePlayerView,或者取消之前的下载请求,否则会出现图片错位的问题。 - 线程安全:所有UI更新操作必须在主线程执行,这也是我们用
dispatch_async(dispatch_get_main_queue(), ...)的原因。
内容的提问来源于stack exchange,提问作者Vishwanath Deshmukh
相关产品推荐
相关产品推荐

