我实现了一个UITmageView UITableView单元,每个单元通过NSTimer每隔5秒定期刷新一次。每个图像都是从后台线程中的服务器加载的,并且从后台线程我也通过调用performSelectorOnMainThread
来更新UI,显示新图像。到目前为止一切都很好。
目前,当我不等到当前单元格中加载图像并快速向下滚动到新单元格时,我遇到了问题。然而,该新单元显示先前单元的图像而不是新单元的图像。虽然下一轮NSTimer会正确显示图像,但这可能会让用户感到困惑。
如果我不重复使用UITableView的单元格,问题就会消失,但考虑到我的应用程序中显示的单元格数量,这不是一个选项。
因此,我能想到的唯一解决方案是取消(或终止)将显示旧图像的后台线程,如果我知道用户执行滚动操作的话。
我想知道这可能不是最好的做法,因此,寻求你的意见。
(我也不能使用SDWebImage,因为我的要求是在从服务器加载的循环中显示一组图像)
// In MyViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
NSTimer* timer=[NSTimer scheduledTimerWithTimeInterval:ANIMATION_SCHEDULED_AT_TIME_INTERVAL
target:self
selector:@selector(updateImageInBackground:)
userInfo:cell.imageView
repeats:YES];
...
}
- (void) updateImageInBackground:(NSTimer*)aTimer
{
[self performSelectorInBackground:@selector(updateImage:)
withObject:[aTimer userInfo]];
}
- (void) updateImage:(AnimatedImageView*)animatedImageView
{
@autoreleasepool {
[animatedImageView refresh];
}
}
// In AnimatedImageView.m
-(void)refresh
{
if(self.currentIndex>=self.urls.count)
self.currentIndex=0;
ASIHTTPRequest *request=[[ASIHTTPRequest alloc] initWithURL:[self.urls objectAtIndex:self.currentIndex]];
[request startSynchronous];
UIImage *image = [UIImage imageWithData:[request responseData]];
// How do I cancel this operation if I know that a user performs a scrolling action, therefore departing from this cell.
[self performSelectorOnMainThread:@selector(performTransition:)
withObject:image
waitUntilDone:YES];
}
-(void)performTransition:(UIImage*)anImage
{
[UIView transitionWithView:self duration:1.0 options:(UIViewAnimationOptionTransitionCrossDissolve | UIViewAnimationOptionAllowUserInteraction) animations:^{
self.image=anImage;
currentIndex++;
} completion:^(BOOL finished) {
}];
}
答案 0 :(得分:1)
好的... 另一种方法是将“图像请求代码”放入您的AnimatedImageView中,并在每次设置新的图像URL时使待处理请求无效
// your controller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
...
[cell.imageView setDistantImage:imageURL];
...
}
然后,在你的Cell类中,'setDistantImage'创建一个ASIHttpRequest,并使前一个无效
//your ImageView class
@property (...) ASIHttpRequest *distantImageRequest;
- (void) setDistantImageUrl:(NSUrl *)imageUrl
{
//clear previous request
[distantImageRequest clearDelegatesAndCancel];
//set a new request, with the callBack embedded directly in this ImageView class
...
}
//animation and callBacks methods in this ImageView class too...
答案 1 :(得分:0)
在CollectionView&amp ;;中加载许多图像时,我遇到了完全相同的问题。带有重用单元格的TableView通过快速和长时间滚动覆盖所有下载的图像。
对于TableView,我解决了它看WWDC 2012,其中提出了'didEndDisplayingCell'方法,但这对没有这种方法的CollectionView没有用。 :O(
最后,我找到了两个解决方案......幸运的是。 我们的想法是创建由TableView或CollectionView中的单元格调用的类 ImageLoader ,并且负责下载图像。 在您的实现中,添加此代码段以将变量隐藏到世界其他地方:
@interface ImageLoader() {
__block NSURLSession *_session;
}
然后,在您的实现中添加以下函数:
- (void)getViewForCell:(UIImageView*)cell
FromURL:(NSURL*)url {
NSBlockOperation *_loadImageOp;
__block NSOperationQueue *_opQueue;
_opQueue = [[NSOperationQueue alloc]init];
if (_session != nil) {
[_session invalidateAndCancel];
}
_loadImageOp = [[NSBlockOperation alloc]init];
[_loadImageOp addExecutionBlock:^{
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:_opQueue];
NSURLSessionDownloadTask *downloadTask = [_session downloadTaskWithURL:url
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
UIImage *imageCell = [UIImage imageWithData:[NSData dataWithContentsOfURL:location]];
if (imageCell) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.image = imageCell;
}];
} else {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
cell.image = [UIImage imageNamed:@"imageNotWorking.png"];
}];
}
}];
[downloadTask resume];
}];
[_opQueue addOperation:_loadImageOp];
}
这个想法是1 /。你创建一个块,其中成员变量_session将通过downloadTask,2 /获取传入参数中给出的url。您在下载时通过mainQueue将图像显示到用户界面... 3 /。将初始创建的块添加到专用于此特定重用单元的队列中。
重要的是如果成员变量不是nil,则调用'invalidateAndCancel'函数,因为每次要重用单元格时,前一个会话都不会将最后一个图像带回去,而是由传入参数'url'。
之后,不要忘记将1 /。下面的代码段放在TableView / CollectionView单元类中,以便每个重用单元只使用一个imageLoader 和2 /。该方法是在CollectionView / TableView的方法'tableView:cellForRowAtIndexPath:'中调用:
@interface TableCell () { //...or CollectionCell :o)
ImageLoader *_imgLoader;
}
@end
@implementation TableCell
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
_imgLoader = [[ImageLoader alloc]init];
return self;
}
- (void)imageURL:(NSURL*)url {
[_imgLoader getViewForCell:self.imageViewWhereTheDownloadedImageIs
FromURL:url];
}
@end
完成后,只需在CollectionView / TableView的方法'tableView:cellForRowAtIndexPath:'中调用[cell imageURL:url]
。
现在,您可以根据需要快速滚动,并且您将始终拥有正确的图像,而且只有这一图像。
我希望它会有所帮助...... :O)