在UITableView中延迟加载多个图像
下载多个图像并在UITmageView内部的UIImageView中加载每个图像的最佳做法是什么? 特别是,我应该在下载后resize/替换UIImageview,还是应该调整图像大小以适应UIImageView。 请注意,resize/替换UIImageView也会resize/替换UITableViewCell。 它会引起任何问题吗?
几点想法:
下载多个图像并在
UIImageView
内部的UIImageView
加载每个图像的最佳做法是什么?
有关下载图像的最佳做法包括:
-
绝对可以利用延迟加载(在需要时加载图像而不是之前加载图像)。
-
异步下载图像。
-
确保您的下载技术将取消对不再可见的tableview单元格的请求。 例如,如果您使用的是慢速网络并在桌面视图中快速向下滚动,则不希望系统下载不再可见的图像。
-
确保您没有为服务器发出太多并发请求。 在iOS中,当您超过5或6个并发请求时,后续请求将冻结,直到前一个请求完成。 在最坏的情况下,后续请求实际上会在超时时开始失败。
-
缓存结果。 至少,将它们缓存在内存中。 您可能还想将它们缓存到持久存储(也称为“磁盘”)。
如果您要为异步操作,缓存等编写自己的代码,您可能希望使用NSOperationQueue
而不是GCD,以便限制后台请求的数量并使请求可取消 。 您将使用NSCache
来缓存图像。 并且您可能使用UITableViewCell
子类(或类别),以便您可以保存对“先前”操作的weak
引用,以便您可以取消任何不完整的先前请求。
正如您所看到的,这是非常重要的,我建议您使用现有的UIImageView
类别,例如可用作SDWebImage
或AFNetworking
一部分。 恕我直言,前者更富有(例如提供磁盘缓存),但如果你做了很多网络并且想要坚持使用单一框架, AFNetworking
也做得很好。
后来你问:
特别是,我应该在下载后resize/替换UIImageview,还是应该调整图像大小以适应UIImageView。 请注意,resize/替换UIImageView也会resize/替换UITableViewCell。 它会引起任何问题吗?
-
如果您的图像大于单元格缩略图视图所需的图像,则有两种方法。 首先,您可以使用
UIViewContentModeScaleAspectFit
或UIViewContentModeScaleAspectFill
的contentMode
(如果您使用AspectFill
,请确保您还将clipsToBounds
设置为YES
)。 更好的是,您可以在下载后实际调整图像大小 。 -
这是个人观点,但我认为在单元格上安装固定大小的
UIImageView
是一个更好的用户体验,然后在完成异步图像下载时,只需设置UIImageView
的image
属性即可。 您希望图像在下载时优雅地显示在UI中,但是当用户已经在阅读那里的内容时,您通常不希望对视图进行重新布局。 如果您的设计绝对需要重新布局单元格,那么您只需调用reloadRowsAtIndexPaths
。
在UITableViewCell
延迟加载图像的常见做法是使用通知回调让UITableViewCell
知道何时接收到图像。
本质上,您将要创建一个UIImageView
的子类,该子类具有imageURL字段,当更改时,它会触发对图像的请求,并使用它而不是标准的UIImageView
:
UIImageView子类的接口:
@property (nonatomic, copy) NSString *imageURL;
UIImageView子类的实现:
//synthesize property @synthesize imageURL = _imageURL; - (void)setImageURL:(NSString *)imageURL { if(_imageURL) [[NSNotificationCenter defaultCenter] removeObserver:self name:_imageURL object:nil]; _imageURL = [imageURL copy]; //if imageURL is valid... if(_imageURL.length) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveImage:) name:_imageURL object:nil]; //fire off some asynchronous image fetch //when the image fetch completes, sent off a notification using the imageURL as the notification name //It's up to you to create the implementation for this yourself ... [MyImageManager fetchImage:_imageURL notificationName:_imageURL]; } } - (void)didReceiveImage:(NSNotification*)notification { //handle your received image here if([notification.object isKindOfClass:[UIImage class]]) { self.myCustomImageView.image = notification.object; } }
当你覆盖prepareForReuse时,在UITableViewCell类中:
- (void)prepareForReuse { [super prepareForReuse]; self.myCustomImageView.imageURL = nil; self.myCustomImageView.image = nil; //do the rest of your prepareForReuse here: ... }
现在,就整个图像重新调整尺寸和调整图像视图的大小而言,你应该做的就是单独保留imageView的大小,并使用contentMode
属性来处理结果图像的不同大小。 您可能的值是:
UIViewContentModeScaleToFill, UIViewContentModeScaleAspectFit, UIViewContentModeScaleAspectFill, UIViewContentModeRedraw, UIViewContentModeCenter, UIViewContentModeTop, UIViewContentModeBottom, UIViewContentModeLeft, UIViewContentModeRight, UIViewContentModeTopLeft, UIViewContentModeTopRight, UIViewContentModeBottomLeft, UIViewContentModeBottomRight,
每个都有各自的结果 – 你可能想要使用UIViewContentModeScaleAspectFit
因为这将重新调整图像的大小以适应imageView,而不会扭曲它 – 这可能会留下空白边距到imageView的大小或顶部/底部。 Aspect填充将执行类似的操作,只是它会将图像调整为足够大以填充整个imageView,并且可能会切掉图像的边或顶部/底部。 缩放以填充将仅拉伸图像以填充imageView。