Iphone UITableView、单元格和通知-通知后单元格未显示正确图像

Iphone UITableView、单元格和通知-通知后单元格未显示正确图像,iphone,objective-c,ios,xcode,Iphone,Objective C,Ios,Xcode,所以我在这个问题上被难住了,不知道最好的方法是什么。我需要从api下载,存储在核心数据中,然后显示tableview的一组单元格,其中只有标题、描述和图片(图片是url,所以我也需要下载)。tableview只有一个部分 我已经设置了一个结构,在我的TableViewController中,我使self.tableview成为NSNotification(在初始化期间指定)的侦听器。我已经创建了一个自定义单元格视图,其中有一个名为firstLoad的额外属性,在初始化过程中设置为YES。在我的t

所以我在这个问题上被难住了,不知道最好的方法是什么。我需要从api下载,存储在核心数据中,然后显示tableview的一组单元格,其中只有标题、描述和图片(图片是url,所以我也需要下载)。tableview只有一个部分

我已经设置了一个结构,在我的TableViewController中,我使self.tableview成为NSNotification(在初始化期间指定)的侦听器。我已经创建了一个自定义单元格视图,其中有一个名为firstLoad的额外属性,在初始化过程中设置为YES。在我的tableView cellForRowAtIndexPath中,我正在检查firstload属性,如果它设置为yes,我将调用一个函数从url下载图像。当发生这种情况时,会发出一个通知,由tableviewcontroller中的函数捕获,我会获取indexpathsforvisiblerows,如果通知中的行与indexpathsforvisiblerows数组中的行匹配,我会设置单元格的图像,然后重新加载数据

我遇到的问题是,当我开始滚动tableview时,看起来有时图像被设置为错误的单元格。这些细胞也被重复使用

我不知道该怎么处理。非常感谢你的帮助

这是CellForRowatineXpath的代码

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  static NSString *CellIdentifier = @"APCell";

  APTableViewGenCell *cell = (APTableViewGenCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  if (cell == nil) {
      cell = [[[APTableViewGenCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier useType:_useType] autorelease];
  }

  // Configure the cell...

  if (APUseTypeGroupsWithEvents == _useType) {
    APGroup *group = [_fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = group.name;
    UIImage *logoImage = [UIImage imageNamed:@"groups.png"];
    if (group.logo.imageURL && (cell.firstLoad == (BOOL *)YES)){
      cell.imageView.image = [logoImage scaleToSize:CGSizeMake(kTableImageWidth, kTableImageHeight)];
      APGroundControl *gc = [APGroundControl sharedGroundControl];
      [gc retrieveImageWithURL:group.logo.imageURL indexPath:indexPath withDefaultImgName:@"groups" andDefaultImgType:@"png" sender:self];
    }


    cell.detailTextLabel.text = group.groupDescription;

    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  }

  return cell;
}
这是收到的通知处理程序:

- (void)imageRetrieved:(NSNotification *)notification {
   NSLog(@"%@", @"NOTIFICATION RECEIVED");
  if ([[[notification userInfo] objectForKey:kNewImageDownloadedSenderKey] isEqual:self]) {
    // If the cell is still visible show the image.
    NSIndexPath *indexPath = [[notification userInfo] objectForKey:kNewImageDownloadedIndexPathKey];
    if (indexPath) {
      NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];
      for (NSIndexPath *path in indexPaths) {
        if (indexPath.row == path.row) {
          APTableViewGenCell *cell = (APTableViewGenCell *)[self.tableView cellForRowAtIndexPath:path];
          NSData *imageData = [[notification userInfo] objectForKey:kNewImageDownloadedDataKey];
          UIImage *logoImage = nil;
          cell.firstLoad = (BOOL *)NO;
          if ([imageData isKindOfClass:[UIImage class]]) {
            logoImage = (UIImage *)imageData; 
            logoImage = [logoImage scaleToSize:CGSizeMake(kTableImageWidth, kTableImageHeight)];
            cell.imageView.image = logoImage; // @todo: get rid of this temp check once all image gets moved over to the new retrieve function
          }
          else {
            logoImage = [UIImage imageWithData:imageData];            
            logoImage = [logoImage scaleToSize:CGSizeMake(kTableImageWidth, kTableImageHeight)];
            cell.imageView.image = logoImage;//[UIImage imageWithData:imageData];
          }
          break;
        }
      }
      [self.tableView reloadData];
    }
  }
}
以及使用URL检索图像的代码:

- (void)retrieveImageWithURL:(NSString *)url indexPath:(NSIndexPath *)indexPathOrNil withDefaultImgName:(NSString *)defaultImgName andDefaultImgType:(NSString *) defaultImgType sender:(id)sender {
  NSLog(@"%@", @"RETRIEVE IMAGE CALLED");
  NSLog(@"%@", indexPathOrNil);
  [self queueBlock:^{
    NSData *imageData = nil;

    // Get image locally
    if ([url isEqualToString:kGetGroupDefaultLogoURL]){
      NSString *filePath = [[NSBundle mainBundle] pathForResource:defaultImgName ofType:defaultImgType];
      imageData = [NSData dataWithContentsOfFile:filePath];
    }
    // @todo: add a default user logo
    else if (url == nil) {
      NSLog(@"%@", @"Default Retrieve");
      NSString *filePath = [[NSBundle mainBundle] pathForResource:defaultImgName ofType:defaultImgType];
      imageData = [NSData dataWithContentsOfFile:filePath];
    }
    else {
      NSLog(@"%@", @"Local Retrieve");
      imageData = [_imageCache objectForKey:url];
    }

    // If local image does not exist get it from the internet
    if (imageData == nil) {
      NSLog(@"%@", @"Remote Retrieve");
      NSLog(@"URL = %@", url);
      imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
      if (imageData == nil) {
        NSString *filePath = [[NSBundle mainBundle] pathForResource:defaultImgName ofType:defaultImgType];
        imageData = [NSData dataWithContentsOfFile:filePath];
      }
      [_imageCache setObject:imageData forKey:url];
    }

    // Send out notification
    NSMutableArray *objects = [NSMutableArray array];
    NSMutableArray *keys = [NSMutableArray array];
    if (indexPathOrNil) {
      [objects addObject:indexPathOrNil];
      [keys addObject:kNewImageDownloadedIndexPathKey];
    }
    [objects addObject:sender];
    [keys addObject:kNewImageDownloadedSenderKey];
    [objects addObject:imageData];
    [keys addObject:kNewImageDownloadedDataKey];
    NSDictionary *userInfo = [NSDictionary dictionaryWithObjects:objects forKeys:keys];

    dispatch_async(dispatch_get_main_queue(), ^{
      NSLog(@"%@", @"NOTIFICATION SENT");
      [[NSNotificationCenter defaultCenter] postNotificationName:kNewImageDownloadedNotification object:self userInfo:userInfo];
    });
  }];
}

可能是从表视图中退出队列的单元保留了它们在前一个生命周期中拥有的映像。尝试在cellForRowAtIndexPath中将imageview图像设置为nil(在将其更新为新图像之前,这可能需要一些时间)。

好的,首先,您使用的
firstLoad
-标志没有意义。您需要知道图像是否已加载,但由于可重用单元无法与特定图像连接,因此这不是实现此目的的方法

你更新了几次帖子,现在还不清楚你现在的问题是什么。因此,我将为您提供一个合适且干净的解决方案: 首先,你需要一个地方来储存你的图像。
NSMutableDictionary
就可以了。使用键存储URL,使用值存储图像。 输入新单元格时,请查看URL是否已在字典中。如果是这样的话,请填写。否则,启动加载过程。如果这样做,还应该在字典中创建一个新条目,用于存储图像的URL和
nil
。这有助于避免两次加载尚未就绪的图像。在“加载完成”方法中,用图像替换
nil
值。 应该是这样。当然,您需要根据您的具体需求定制这些步骤,但这应该不是什么大问题。希望这有帮助


另外,你应该检查你的代码以找到多余的或者过于复杂的调用。在不知道具体设置的情况下,在
nsindepath
上迭代看起来很奇怪,您应该能够直接访问您的值。

这听起来好像填充重用的单元格有点问题。但是,如果没有任何代码,将很难帮助您。cellForRowAtIndexPath的代码和所有属于它的东西都会非常有用,我想。@Phlibbo刚刚用代码示例更新过!谢谢你看!!嗯,坦率地说,你的代码看起来有点凌乱,但我正在努力理解它。你知道重复使用细胞的工作原理,是吗?即使表中有100行,也可能只有6个单元格,它们被反复使用。所以,我认为你的“首次使用”财产有问题。您可以使用它来查看图像是否已加载,对吗?但是,如果您只有6个单元格(具有6个“首次使用”属性)但有100个图像,则会出现问题:)我希望我在这里是正确的,可能还有其他错误,但请先看一看。@Phlibbo完全同意混乱的评论。我是iphone n00b:)我完全同意你的说法,这是一个bug,我可能只是使用了一个不正确的设计模式。但我使用该模式的原因是,否则它会被抛出到一个循环中,在这个循环中,它不断地调用检索函数并重新加载视图,然后再次调用它。你能想个办法吗?嘿。今天晚些时候,我会发布一个正确的答案。简而言之:创建一个NSDictionary并在其中存储URL图像对。这样,如果图像已经加载,您只需查看dict即可。存储url nil,如果您当前正在加载它。啊,但是如果我在单元格上将图像设置为nil,那么我必须将单元格的firstLoad prop也设置为YES,以便可以加载图像。这将使控制器陷入无休止的循环首先,非常感谢您的响应。但是,如果您注意到,这正是上面retrieveImageWithUrl函数中所做的。不过,通过在tableview本身上创建imageCache属性并通过indexpath对其进行键控,我确实让它工作了。这样,retrieveImageWithUrl永远不会运行,除非该索引路径没有对象。我把第一批货处理掉了,你说得对。另外,lol如何直接从indexpath而不是for循环中提取值?我看了一下文档,找不到一个好的解决方案。