UICollectionView滚动性能使用AFNetworking加载图片
Posted
技术标签:
【中文标题】UICollectionView滚动性能使用AFNetworking加载图片【英文标题】:UICollectionView scroll performance using AFNetworking to load images 【发布时间】:2013-09-02 14:36:01 【问题描述】:我已经阅读了很多关于滚动不良的 UICollectionView 帖子,但似乎没有一个可以直接应用,或者它们仍然没有得到答复。
我正在使用AFNetworking
将图像(95 平方像素)异步加载到每个单元格上,然后当图像再次滚动到视图中时,图像会从缓存中恢复(由 0 给出的响应代码验证) 200)。
这是我尝试过的:
注释掉了weakCell.photoView.image = image;
,因此图像不会在屏幕上绘制并且滚动更流畅(在 HTTP 获取期间仍然有点卡顿)
从 cellForRowAtIndexPath
方法中删除了所有 AFNetworking 代码,滚动更加流畅(即使自定义单元格阴影等仍在屏幕上绘制)
当我在屏幕上仅绘制单元格视图(带有阴影)时,滚动 100 个单元格时非常流畅。一旦我开始在屏幕上绘制图像,我的设备上的滚动效果就很差,甚至在模拟器上也很明显。 Instagram 在他们的个人资料视图上可以非常流畅地滚动数百个单元格,所以我试图接近他们的表现。
有什么方法可以改进下面的代码以提高滚动性能?
这是我的手机密码:
#import "PhotoGalleryCell.h"
@implementation PhotoGalleryCell
- (id)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
// Setup the background color, shadow, and border
self.backgroundColor = [UIColor colorWithWhite:0.25f alpha:1.0f];
self.layer.borderColor = [UIColor blackColor].CGColor;
self.layer.borderWidth = 0.5f;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 3.0f;
self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f);
self.layer.shadowOpacity = 0.5f;
// Make sure we rasterize for retina
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.layer.shouldRasterize = YES;
// Add to the content view
self.photoView = [[UIImageView alloc] initWithFrame:self.bounds];
[self.contentView addSubview:self.photoView];
return self;
- (void)prepareForReuse
[super prepareForReuse];
self.photoView.image = nil;
self.largeImageURL = nil;
这是我的 UICollectionView 代码:
#pragma mark - Collection View Delegates
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
return 1;
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
return [zePhotos count];
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
PhotoGalleryCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPGPhotoCellIdentifier forIndexPath:indexPath];
// Get a reference to the image dictionary
NSDictionary *photoDict = [[zePhotos objectAtIndex:indexPath.row] objectForKey:@"image"];
// Asynchronously set the thumbnail view
__weak PhotoGalleryCell *weakCell = cell;
NSString *thumbnailURL = [[photoDict objectForKey:@"thumbnail"] objectForKey:@"url"];
NSURLRequest *photoRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:thumbnailURL]];
[cell.photoView setImageWithURLRequest:photoRequest
placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
weakCell.photoView.image = image;
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error)
NSLog(@"Error retrieving thumbnail... %@", [error localizedDescription]);
];
// Cache the large image URL in case they tap on this cell later
cell.largeImageURL = [[photoDict objectForKey:@"large"] objectForKey:@"url"];
return cell;
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
[self performSegueWithIdentifier:@"showPhotoDetail" sender:self];
【问题讨论】:
您是否成功解决了这个问题?我遇到了同样的事情,如果您有后续工作,我将不胜感激。 @Tres 抱歉,我现在只能接受性能下降。 AFNetworking 表示它已针对 UIScrollView 的任何子类(例如 UITableView 或 UICollectionView)进行了优化,但这似乎是 UI 性能不佳的原因。 【参考方案1】:您可以尝试在单元格初始化中添加 shadowPath,它应该会提高性能,这是我在我的一个项目中使用的代码来添加圆形 shadowPath(请参阅 UIBezierPath 方法以获取更多选择)
self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.frame.bounds
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(10, 10)].CGPath;
此外,如果我没记错 AFNetworking 不会调整从服务器返回的图像的大小,因此它可能会影响图像的质量(尽管您在 UIImageView 中添加了缩放方法),我建议调度返回的图像如果你愿意,可以调整它的大小:
CGSize targetSize = cell.photoView.bounds.size;
[cell.photoView setImageWithURLRequest:photoRequest
placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
CGFloat imageHeight = image.size.height;
CGFloat imageWidth = image.size.width;
CGSize newSize = weakCell.imageView.bounds.size;
CGFloat scaleFactor = targetSize.width / imageWidth;
newSize.height = imageHeight * scaleFactor;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *small = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(),^
weakCell.photoView.image = small;
);
);
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error)
NSLog(@"Error retrieving thumbnail... %@", [error localizedDescription]);
];
【讨论】:
感谢您的回答。正如我在 OP 中所说:当我在屏幕上仅绘制单元格视图(带有阴影)时,滚动 100 个单元格时非常平滑。一旦我开始在屏幕上绘制图像,我的设备上的滚动效果就很差,甚至在模拟器上也很明显。因此,这似乎是 AFNetworking 图像缓存的问题。【参考方案2】:代码检查看起来不错,但我敢打赌,阴影的合成大大增加了延迟。找出导致延迟的确切原因的方法是使用 Instruments 中的 Time Profiler 工具。 Here are the docs from Apple。
【讨论】:
当我仅在屏幕上绘制单元格视图(带有阴影)时,滚动 100 个单元格时非常流畅。一旦我开始在屏幕上绘制图像,我的设备上的滚动效果就很差,甚至在模拟器上也很明显。 Instagram 在他们的个人资料视图上可以非常流畅地滚动数百个单元格,所以我试图接近他们的表现。【参考方案3】:问题是当您快速滚动时,您会同时启动数百个网络请求。如果您缓存了图像,请立即显示它。如果不这样做,请仅在表格视图变慢时开始下载。
你可以这样使用:
//Properties or Instance Variables
NSDate *scrollDateBuffer;
CGPoint scrollOffsetBuffer;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
NSTimeInterval secondsSinceLastScroll = [[NSDate date] timeIntervalSinceDate:scrollDateBuffer];
CGFloat distanceSinceLastScroll = fabsf(scrollView.contentOffset.y - scrollOffsetBuffer.y);
BOOL slow = (secondsSinceLastScroll > 0 && secondsSinceLastScroll < 0.02);
BOOL small = (distanceSinceLastScroll > 0 && distanceSinceLastScroll < 1);
if (slow && small)
[self loadImagesForOnscreenRows];
scrollDateBuffer = [NSDate date];
scrollOffsetBuffer = scrollView.contentOffset;
你会想用其他方法调用loadImagesForOnscreenRows
,比如当新数据进来时,viewWillAppear
和scrollViewDidScrollToTop
。
这是loadImagesForOnscreenRows
的示例实现:
- (void)loadImagesForOnscreenRows
@try
for (UITableViewCell *cell in self.tableView.visibleCells)
// load your images
NSURLRequest *photoRequest = …;
if (photoRequest)
[cell.photoView setImageWithURLRequest:…];
@catch (NSException *exception)
NSLog(@"Exception when loading table cells: %@", exception);
我在 try/catch 块中有这个,因为根据我的经验 [UITableView -visibleCells]
不可靠 - 它偶尔会返回已释放的单元格或没有超级视图的单元格。如果您确保仅在表格没有快速滚动时调用此方法,则它不会对滚动性能产生太大影响。
另外,请注意 AFNetworking UIImageView 类别不公开缓存对象。您需要稍微修改它以检查您是否已经缓存了图像; this answer 应该为您指明正确的方向。
【讨论】:
请注意,我的回答包括有关 UITableView 的信息,并且您使用的是 UICollectionView。同样的答案也适用,因为 UICollectionView 遵循相同的设计模式。 抱歉回复晚了! AFNetworking 应该已经针对 UIScrollView 的任何子类(例如您列出的子类)进行了优化,因此应该已经处理好了。以上是关于UICollectionView滚动性能使用AFNetworking加载图片的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView 的滚动性能不佳 - 分析指向可访问性调用