集合视图滚动动画导致错误的图像位置

时间:2014-09-21 12:58:30

标签: ios objective-c

我必须在集合视图中重新加载数据,以便根据新数据源设置新的单元格大小。 比我必须滚动到开头,我想用动画做到这一点。 每一行都有1个单元格。

所以,使用这个:

    [self.collectionView reloadData];
    [self.collectionView setContentOffset:CGPointZero animated:YES];

发生的事情是它滚动到开始并重新加载,但是当他来到图像1时,我看到许多图像被快速替换,并且它会停留在某些不属于第一个单元格的图像上,而是出现在其他图像上细胞。

如果我滚动到开始 - 没有动画(nimated:NO),则会发生。 我需要那个动画。 什么可能导致这个问题?

修改 当我快速滚动时,我可以看到类似的问题,我可以看到在他们转向应该加载的最终图像之前在他们的单元格中变化非常快的图像。

3 个答案:

答案 0 :(得分:1)

问题在于细胞被重复使用,

让我们看一下这个例子

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"YourCellId"];
    cell.image = nil;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        UIImage *image = [self imageForIndexPathRow:@(indexPath.row)];
       dispatch_async(dispatch_get_main_queue(), ^{
           cell.image = image;
       });
    });
    return cell;
}  

由于操作是异步的,因此快速滚动将导致为1个单元分派2个操作。

例如,让我们命名一些单元格指针“ImageCell-1”
现在让我们看看,如果你开始快速滚动,会发生什么,并且会调度2个操作 第一个操作加载“a.png”,下载激活,你快速滚动,“ImageCell-1”现在正在加载“b.png”(但“a.png”仍在加载)。

所以“a.png”完成下载并设置为“ImageCell-1”,然后完成“b.png”并设置为“ImageCell-1”。
(这是你看到的问题=>“当我快速滚动时,我可以看到类似的问题,我可以看到在他们转向应该加载的最终图像之前在他们的单元格中变化非常快的图像。 “)

但也可能是这种情况(当您使用像AFNetworking这样的“高级库”时,当队列由它们管理,并且可以进行并发下载时)“b.png”完成下载并将其设置为“ImageCell-1”,之后完成“a.png”下载并设置为“ImageCell-1”。这将导致看到是“a.png”而不是“b.png”。

解决方案是取消下载操作,在开始新操作之前,如果您正在使用AFNetworking,请保留NSOperations,并在开始新下载之前致电[operation cancelOperation];

修改
如果您正在使用NSURLConnection,则需要切换到使用NSURLSession的现代方式(以支持请求取消)。

这里我写了一个小方法来解决你的问题

@interface MyCell ()

@property (strong, nonatomic) NSURLSessionDataTask *dataTask;

@end

- (void)loadImageFromPath:(NSString *)aPath
           availableCache:(NSCache *)aCache
{
    if (self.dataTask) {
        [self.dataTask cancel];
    }

    NSData *cache = [aCache objectForKey:aPath];
    if (cache) {
        self.image = [UIImage imageWithData:cache];
        return;
    }
    self.dataTask = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:aPath]
                                      completionHandler:^(NSData *data,
                                                          NSURLResponse *response,
                                                          NSError *error)
                                {
                                    if (data) {
                                        self.image = [UIImage imageWithData:data];
                                        self.dataTask = nil;

                                        // Add to cache
                                        [aCache setObject:data forKey:aPath];
                                    }
                                }];
    [self.dataTask resume];
}

将此方法放在您的单元格中,

并从cellForRow调用它,就像这样

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"YourCellId"];
    [cell loadImageFromPath:imagePath availableCache:self.cache];
    return cell;
} 

答案 1 :(得分:1)

我认为我解决了这个问题,或者改进了很多东西。

当你开始从网上下载时,给你的块一个标签:

- (void)downloadImageWithURL:(NSURL *)url AndTag:(int)Gtag completionBlock:(void (^)(BOOL succeeded, NSData *data,int tag))completionBlock
{
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    int thetag=Gtag;
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
     {
         if (!error)
         {

             completionBlock(YES, data,thetag);
         }
         else
         {

             completionBlock(NO, nil,thetag);
         }
     }];
}

然后,当下载完成时,我们将检查该单元格现在是否在屏幕上,如果不是,我们将不会将其图像加载到单元格中:

  [self downloadImageWithURL:url AndTag:(int)cell.tag  completionBlock:^(BOOL succeeded, NSData *data,int tag)
         {

     BOOL isvisible=0;
                 for (iPhoneCostumCell *tcell in [self.collectionView visibleCells])
                 {
                    if(tcell.tag==tag)
                    {
                        isvisible=1;
                        break;
                    }
                 }

我认为这是一个很好的解决方案,它使事情更加稳定。它还使处理器工作量减少,因为如果我们找不到图像,我们就不会继续在另一个线程中进行NSData转换。

答案 2 :(得分:0)

[UIView animateWithDuration:1.0 delay:0.0 options: UIViewAnimationOptionCurveEaseIn animations:^{
        [self.collectionView setContentOffset:CGPointZero animated:NO];
    } completion:nil];
}];

尝试制作自己的动画。