2

我的代码在方法中使用InternetImage异步下载图像,tableview: cellForRowAtIndexPath:方法是使用 initWithURL 初始化 IntentImage 并调用 downloadImage。此代码在向下滚动到 UITableView 中的新单元格 UITableViewCell 后完美运行,但不是之前,即使 URL 在两种情况下都是正确的。没有调用 InternetImage 的 NSURLConnection 委托方法来通知我连接成功或失败,因为它们应该是。由于图像下载失败,调用reloadData:,setNeedsDisplay:setNeedsLayout:什么也不做。

这是我的 UiTableViewController 子类中的代码:

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        Object *object = (Object *)[self.array objectAtIndex:indexPath.row];

        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
        if (cell == nil) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"] autorelease];
        }

        cell.textLabel.text = object.attribute;

        if (object.image == nil && object != nil) {
            NSString *URLString = [[MyAppDelegate imageURLFromLink:(object.link) withExtension:@".jpg"]absoluteString];

            InternetImage *asynchImage = [[InternetImage alloc] initWithUrl:URLString object:object];
            [asynchImage downloadImage:self];
        }

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

        if (object.image != nil && object.thumbnailImage == nil) {

            UIImage *image= (UIImage *) object.image;
            UIImage *thumbnail = [image _imageScaledToSize:CGSizeMake(75.0, 50.0) interpolationQuality:20];
            object.thumbnailImage = thumbnail;
        }
        cell.imageView.image = object.thumbnailImage;

        return cell;
    }
4

7 回答 7

2

一个对我有用的解决方案是将下载的责任转移到对象,并将处理图像刷新的责任转移到表格单元格本身。如果对象在屏幕上可见并且具有相应的表格单元格,则图像将在下载后立即刷新。

这种方法需要进行以下更改:

  • 将下载图像的责任转移到 Object 中。
  • 修改对象以在图像更改时发送事件/调用委托/通知属性已更改。
  • 子类 UITableViewCell 以了解它将显示的对象,该对象在分配时将其自身作为更改委托添加到对象。

这是如何工作的草图:

// in the UITableViewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  Object *object = (Object *)[self.array objectAtIndex:indexPath.row];

  ObjectTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"objectcell"];
  if (nil == cell) {
    cell = [[[ObjectTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"objectcell"] autorelease];
  }

  cell.object = object;

  return cell;
}

// in Object

- (void) downloadImageIfNeeded {
  if (nil == image && nil == internetImage) {
     NSString *URLString = [[MyAppDelegate imageURLFromLink:(object.link) withExtension:@".jpg"]absoluteString];

     internetImage = [[InternetImage alloc] initWithUrl:URLString object:object];
     [internetImage downloadImage:self];
  }
} 

- (void) internetImageReady:(InternetImage*)downloadedImage {
  self.image = downloadedImage.image;
  self.thumbnailImage = [image _imageScaledToSize:CGSizeMake(75.0, 50.0) interpolationQuality:20];

  // notify the delegate
  [imageDelegate imageChanged: self];
}

// in ObjectTableViewCell.h
@property (nonatomic, retain) Object *object;

// in ObjectTableViewCell.m
- (Object *) object {
  return object;
}

- (void) setObject: (Object *) obj {
  if (obj != object) {
    object.imageDelegate = nil;
    [object release];
    object = nil;

    if (nil != object) {
      object = [obj retain];
      object.imageDelegate = self;

      [object downloadImageIfNeeded];
      self.textLabel.text = object.attribute;
      self.imageView.image = object.thumbnailImage;

      self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
   }
}

- (void) imageChanged: (Object *) o {
  self.imageView.image = object.thumbnailImage;
}
于 2009-08-08T15:52:03.137 回答
1

如果您问为什么加载表格后没有在后台下载图像,那是因为tableView:cellForRowAtIndexPath:仅使用indexPaths屏幕单元格调用。

如果您希望一次下载所有图像,而不是当一个单元格在屏幕上滚动时下载,请编写一个方法来循环您的array属性并在其中实例化InternetImage对象。一个可能的地方可能是在viewDidLoad

然后tableView:willDisplayCell:forRowAtIndexPath:在您的 UITableView 委托中实现并使其负责将正确的InternetImage对象分配给每个单元格的imageView属性。

于 2009-08-08T06:08:15.417 回答
1

只要表格正在滚动,似乎 NSUrlConnections 就不会检索数据。一旦滚动停止,它会立即继续检索数据。然而,这种行为可以在任何 iPhone 应用程序中看到(只要表格在滚动,就不会加载图像),最后,我决定在我的应用程序中也使用它。我认为,Apple 是故意这样做的。如果用户滚动速度非常快,也许可以防止设备向服务器发送大量图像请求;或者甚至可以随时为用户保持平滑滚动。

但是,如果它至少可以在缓慢滚动时工作并且仅在快速滚动时阻止下载,那就太好了。

于 2009-08-12T20:37:58.320 回答
0

我实际上花了一些时间查看代码。

InternetImage我注意到有这个功能。你要做的就是让这个函数通知你TableView“刷新”。您可以通过将委托函数传递给稍后调用的类来做到这一点,但现在事情变得非常棘手。因为当您离开视图时,您需要将要下载的每个图像的委托函数设置为“nil”。

-(void) internetImageReady:(InternetImage*)downloadedImage
{   
    // The image has been downloaded. Put the image into the UIImageView
    [imageView setImage:downloadedImage.Image];
}

当你得到答案时,请在这里发布答案,我很想看看这是否解决了它。

于 2009-08-08T06:28:53.753 回答
0

很好的方法?首先检查你在哪里分配 imageview?第二,如果您的图像是固定的,则意味着特定于整个代码

if(cell == nil)
{

}

侧那个条件。我面临同样的问题并像这样解决了。让我知道这是否有效。

于 2009-08-08T12:14:43.093 回答
0

好的,首先,您在这里发布的不是您正在使用的,或者您对未提及的 InternetImage 进行了修改,因为 InternetImage 不响应 initWithUrl:object:。您还没有将代码包含到委托方法 internetImageReady: 中。我知道当出现问题时它不会被调用,但是一旦你开始滚动它就会被调用,并且在正确的情况下发生的对比将有助于人们诊断你的问题。

此外,您的描述并不完全清楚滚动时会发生什么。显然随后的调用有效,但是所有先前尝试的连接是否突然开始,或者它们完全丢失了?

至于发生了什么,出于上述原因,我认为除了给你一些猜测之外,没有人能做更多的事情。如果我不得不打赌,如果我在上一段中所说的正在发生,那么你的问题是(无论出于何种原因)主线程 runloop 不会处理 NSURLConnections 事件,直到有东西启动泵。这可能有多种原因,例如运行循环处于错误模式。您可以尝试手动运行 NSRunLoop,或更改为 InternetImage 以显式设置运行循环,或手动启动下载。

两个快速的旁白

  1. InternetImage 似乎做了很多习惯上与一般 Cocoa 编程约定不一致的事情。至少对我来说,这使得快速阅读变得更加困难,而且我不完全确定它是否正确地做事。
  2. 不要使用 _imageScaledToSize:。这是一种私有的 Apple 方法,如果他们注意到您正在使用它,可能会导致拒绝。
于 2009-08-19T07:55:24.117 回答
0

我没有答案,但我可以告诉你,我遇到了同样的问题。

我正在使用 Mark Johnson 的AsyncImageView类的略微修改版本。图像的异步请求不会导致调用任何委托,直到我滚动 tableview。一旦我将一个单元格滚动出视图并返回,呼叫就会再次触发并正确获取数据。

我在其他地方读到 NSURLConnection 取决于运行循环处于某种模式 - 我不知道这是否适用于在 tableView:cellForIndexPath 中启动 URLConnection 请求的情况。

如何调试 NSURLConnection 内部?

[更新]

好的,所以我通过显式启动主线程上的连接解决了这个问题的特定实例。我将连接设置代码放在不同的选择器中,并通过 performSelectorOnMainThread 调用它,一切都很好。

您应该尝试在启动连接的选择器中记录当前的运行循环模式 - 我发现这不是 NSUrlConnection 需要的默认运行循环模式。

于 2009-10-15T18:16:18.140 回答