UICollectionViewController 会暂时显示错误的图像,然后刷新到正确的图像
UICollectionViewController would show me a wrong image for a moment and then refresh to the right one
我正在根据 JSON 响应创建集合视图。 Json 响应具有高分辨率图像和 2000 年左右的图像数量。由于数据来自第三方,我无法控制图像大小。
实施:
JSON 样本:
{
"imagelist": [
{
"largeImage": "https://amazingphotos/01.jpg"
},
{
"largeImage": "https://amazingphotos/02.jpg"
},
}
正在访问 UICollectionViewController 中的 JSON:
Alamofire.request(.GET, dataURL)
.responseJSON { _, _, result in
if var json:JSON = JSON(result.value!){
self.imageURLs = json["imagelist"].arrayValue
self.collectionView?.reloadData()
}
}
在 cellForItemAtIndexPath 中,我将 URL 传递给单元格而不是图像本身。
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("imageCell", forIndexPath: indexPath) as! CollectionViewCell
cell.imageData = imageURLs[indexPath.row]
在自定义 Cell 中,我只是这样做:
var imageData:SwiftyJSON.JSON?{
didSet{
self.setup()
}
}
func setup(){
if let imageURLString = self.imageData?["largeImage"]{
let imageURL = NSURL(string: imageURLString.stringValue)
self.imageThumbnail.hnk_setImageFromURL(imageURL!)
}
问题:当我加载集合视图时,它会正确显示图像,一旦我开始滚动,它就会在新单元格上显示以前的图像几秒钟,然后刷新。
我曾多次使用集合视图,但从未处理过如此多的数据。请指教。
TIA
编辑:
如果您还没有实现缓存,将图像 URL 而不是图像传递给单元格通常不是一个好习惯。
您可以尝试通过以下方法之一解决此问题:
nil
在 prepareForReuse
中取出 imageView
的图像(按照建议
通过 ChrisH
)
- 一张一张地下载图像并将它们存储在一个数组中。下载图像后,调用:
collectionView.reloadItemsAtIndexPaths(indexPaths);
那个
特定的细胞。
- 重构代码并实现图像缓存。
如果您在实现图像缓存方面需要帮助(使用 NSCache
),遵循 Objective-C
代码可能会给您一个想法:
@interface MBGalleryViewController ()
{
NSArray* imageUrls; // array of image urls
NSCache* cache;
}
@end
@implementation MBGalleryViewController
- (void)viewDidLoad
{
[super viewDidLoad];
cache = [[NSCache alloc] init]; // initialize cache
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [imageUrls count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifier = @"Cell";
MyCollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier
forIndexPath:indexPath];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
UIImage* image = [cache objectForKey:@(indexPath.item)]; // retrieve image for this indexPath.item
if (!image) { // will be true if is not yet cached
image = [imageDownloader downloadImageWithURL:imageUrls[indexPath.item]]; // download image from somewhere
[cache setObject:image forKey:@(indexPath.item)]; // cache image for this indexPath.item
}
dispatch_sync(dispatch_get_main_queue(), ^(void) {
cell.imageView.image = image; // set image
});
});
return cell;
}
@end
原版Post
我过去也遇到过类似的问题。
这是因为 cellForItemAtIndexPath
在图像实际下载之前被调用,或者可能是因为图像未在主队列中更新。
此问题的解决方法是在 UICollectionViewCell
.
中为您的 ImageView
设置默认图像
也可以尝试像这样设置图像:
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = imageURLs[indexPath.row];
}
希望这对您有所帮助!
您在滚动集合视图时看到的是一个重复使用的单元格,其中包含以前使用的图像。 UICollectionViewCell
有一个 prepareForReuse
函数,您可以使用该函数将任何子视图设置回默认状态。
override func prepareForReuse() {
myImageView.image = nil
super.prepareForReuse()
}
我正在根据 JSON 响应创建集合视图。 Json 响应具有高分辨率图像和 2000 年左右的图像数量。由于数据来自第三方,我无法控制图像大小。
实施:
JSON 样本:
{
"imagelist": [
{
"largeImage": "https://amazingphotos/01.jpg"
},
{
"largeImage": "https://amazingphotos/02.jpg"
},
}
正在访问 UICollectionViewController 中的 JSON:
Alamofire.request(.GET, dataURL)
.responseJSON { _, _, result in
if var json:JSON = JSON(result.value!){
self.imageURLs = json["imagelist"].arrayValue
self.collectionView?.reloadData()
}
}
在 cellForItemAtIndexPath 中,我将 URL 传递给单元格而不是图像本身。
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("imageCell", forIndexPath: indexPath) as! CollectionViewCell
cell.imageData = imageURLs[indexPath.row]
在自定义 Cell 中,我只是这样做:
var imageData:SwiftyJSON.JSON?{
didSet{
self.setup()
}
}
func setup(){
if let imageURLString = self.imageData?["largeImage"]{
let imageURL = NSURL(string: imageURLString.stringValue)
self.imageThumbnail.hnk_setImageFromURL(imageURL!)
}
问题:当我加载集合视图时,它会正确显示图像,一旦我开始滚动,它就会在新单元格上显示以前的图像几秒钟,然后刷新。
我曾多次使用集合视图,但从未处理过如此多的数据。请指教。 TIA
编辑:
如果您还没有实现缓存,将图像 URL 而不是图像传递给单元格通常不是一个好习惯。
您可以尝试通过以下方法之一解决此问题:
nil
在prepareForReuse
中取出imageView
的图像(按照建议 通过ChrisH
)- 一张一张地下载图像并将它们存储在一个数组中。下载图像后,调用:
collectionView.reloadItemsAtIndexPaths(indexPaths);
那个 特定的细胞。 - 重构代码并实现图像缓存。
如果您在实现图像缓存方面需要帮助(使用 NSCache
),遵循 Objective-C
代码可能会给您一个想法:
@interface MBGalleryViewController ()
{
NSArray* imageUrls; // array of image urls
NSCache* cache;
}
@end
@implementation MBGalleryViewController
- (void)viewDidLoad
{
[super viewDidLoad];
cache = [[NSCache alloc] init]; // initialize cache
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [imageUrls count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifier = @"Cell";
MyCollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier
forIndexPath:indexPath];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void) {
UIImage* image = [cache objectForKey:@(indexPath.item)]; // retrieve image for this indexPath.item
if (!image) { // will be true if is not yet cached
image = [imageDownloader downloadImageWithURL:imageUrls[indexPath.item]]; // download image from somewhere
[cache setObject:image forKey:@(indexPath.item)]; // cache image for this indexPath.item
}
dispatch_sync(dispatch_get_main_queue(), ^(void) {
cell.imageView.image = image; // set image
});
});
return cell;
}
@end
原版Post
我过去也遇到过类似的问题。
这是因为 cellForItemAtIndexPath
在图像实际下载之前被调用,或者可能是因为图像未在主队列中更新。
此问题的解决方法是在 UICollectionViewCell
.
中为您的 ImageView
设置默认图像
也可以尝试像这样设置图像:
dispatch_async(dispatch_get_main_queue()) {
cell.imageView.image = imageURLs[indexPath.row];
}
希望这对您有所帮助!
您在滚动集合视图时看到的是一个重复使用的单元格,其中包含以前使用的图像。 UICollectionViewCell
有一个 prepareForReuse
函数,您可以使用该函数将任何子视图设置回默认状态。
override func prepareForReuse() {
myImageView.image = nil
super.prepareForReuse()
}