Iphone UITableView, cell和notifications - cell在通知后没有显示正确的图像



所以我被这个问题难住了,不知道最好的方法是什么。我需要从api下载,存储在core data,然后显示一组单元格的tableview只有一个标题,描述和图片(图片是一个url,所以我需要下载)。tableview只有一个section

我已经在我的TableViewController中设置了一个结构,我正在制作self。tableview是一个NSNotification的监听器(在init中指定)。我已经创建了一个自定义单元格视图,具有一个名为firstLoad的额外属性,在初始化期间被设置为YES。在我的tableView cellForRowAtIndexPath中,我检查firstload属性,如果它设为yes,我调用一个函数从url下载图像。当这种情况发生时,发出通知,由tableviewcontroller中的函数捕获,我得到indexpathsforvisiblerows如果来自通知的行与来自indexpathsforvisiblerows数组之一的行匹配,我设置单元格的图像,然后reloadData

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

我不知道该如何处理这个问题。非常感谢你的帮助!!

这是cellForRowAtIndexPath的代码

- (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];
    }
  }
}

和retrieveImageWithUrl:

的代码
- (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];
    });
  }];
}

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

你更新了你的帖子几次,到现在为止,它不是很清楚你目前的问题是什么。因此,我将为您提供一种适当而干净的解决方案:首先,您需要一个地方来存储您的图像。一个NSMutableDictionary就可以了。使用键来存储url,使用值来存储图像。当您输入一个新的单元格时,查看URL是否已经在字典中。如果有,填上即可。否则,启动加载进程。如果这样做,还应该在字典中创建一个新条目,该条目存储图像的URL和nil。这可以帮助您避免两次加载尚未准备好的图像。在"Loading complete"方法中,您将nil值替换为您的映像。应该就是这个了。当然,您需要根据自己的具体需求定制这些步骤,但这应该不是什么大问题。希望这对你有帮助!

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

可能是从表视图中取出的单元格保留了它们之前的图像。尝试在cellForRowAtIndexPath中将imageview图像设置为nil(在将其更新为新图像之前,这可能需要一些时间)。

最新更新