Мой код загружает изображения асинхронно с использованием InternetImage в методе tableview: cellForRowAtIndexPath: путем инициализации IntentImage с initWithURL и вызовом downloadImage. Этот код отлично работает после прокрутки до новой ячейки UITableViewCell в UITableView, но не раньше, даже если URL-адрес правильный в обоих случаях. Ни один из методов делегата NSURLConnection InternetImage не вызывается, чтобы уведомить меня об успехе или неудаче соединения, как должно быть. Вызов 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;
    }
2
Matthew Bischoff 8 Авг 2009 в 09:13

7 ответов

Лучший ответ

Решение, которое сработало для меня, - переложить ответственность за загрузку на 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;
}
2
dmercredi 8 Авг 2009 в 15:52

Если вы спрашиваете, почему ваши изображения не загружаются в фоновом режиме после загрузки таблицы, это потому, что tableView:cellForRowAtIndexPath: вызывается только с indexPaths ячеек на экране.

Если вы хотите, чтобы все ваши изображения загружались сразу, а не тогда, когда ячейка катится на экране, напишите метод, который циклически перебирает ваше свойство array и вызывает там объекты InternetImage. Вероятное место для этого может быть в viewDidLoad.

Затем реализуйте tableView:willDisplayCell:forRowAtIndexPath: в своем делегате UITableView и возложите на него ответственность за присвоение правильного объекта InternetImage свойству imageView каждой ячейки.

1
firstresponder 8 Авг 2009 в 06:08

Действительно кажется, что NSUrlConnections не извлекают данные, пока таблица прокручивается. Как только прокрутка останавливается, поиск данных продолжается. Однако такое поведение можно увидеть в любом приложении для iPhone (изображения не загружаются, пока таблица прокручивается), и, в конце концов, я решил жить с этим и в своем приложении. Думаю, Apple сделала это специально. Возможно, чтобы устройство не забивало сервер тоннами запросов изображений, если пользователь прокручивает очень быстро; или, возможно, даже для обеспечения плавной прокрутки для пользователя в любое время.

Однако было бы неплохо, если бы он хотя бы работал при медленной прокрутке и блокировал загрузку только при быстрой прокрутке.

1
Zargony 12 Авг 2009 в 20:37

Хорошо, во-первых, то, что вы разместили здесь, не является тем, что вы используете, или вы внесли изменения в InternetImage, о которых не упомянули, поскольку InternetImage не отвечает на initWithUrl: object :. Вы также не включили код в метод делегата internetImageReady :. Я понимаю, что он не вызывается, когда что-то идет не так, но он вызывается, когда вы начинаете прокрутку, и контрастирование того, что происходит в правильном случае, поможет людям диагностировать вашу проблему.

Кроме того, из вашего описания не совсем понятно, что происходит при прокрутке. Очевидно, что последующие вызовы работают, но все ли ранее предпринятые попытки подключения внезапно начинаются в этой точке или они полностью потеряны?

Что касается того, что происходит, то по причинам, указанным выше, я не верю, что кто-то может сделать больше, чем дать вам некоторые предположения. Если бы мне пришлось предположить, что если происходит то, что я сказал в последнем абзаце, то ваша проблема в том, что (по какой-либо причине) цикл выполнения основных потоков не обрабатывает события NSURLConnections, пока что-то не запустит насос. Это могло быть по ряду причин, например, цикл выполнения находится в неправильном режиме. Вы можете попробовать запустить NSRunLoop вручную или перейти на InternetImage, чтобы явно установить цикл выполнения, или вручную запустить загрузку.

Два быстрых отступления

  1. InternetImage, похоже, делает много вещей, которые идиоматически несовместимы с общими соглашениями о программировании какао. По крайней мере, для меня это затрудняет быстрое чтение, и я не совсем уверен, что он все делает правильно.
  2. Не используйте _imageScaledToSize :. Это частный метод Apple, и если они заметят, что вы его используете, это может привести к отказу.
0
Louis Gerbarg 19 Авг 2009 в 07:55

У меня нет ответа, но я могу вам сказать, что я наблюдаю такую ​​же проблему.

Я использую слегка измененную версию класса AsyncImageView Марка Джонсона. Асинхронные запросы изображений не вызывают вызова делегатов, пока я не прокручу табличное представление. Как только я прокручиваю ячейку из поля зрения и обратно, вызов срабатывает снова и правильно получает данные.

Я читал в другом месте, что NSURLConnection зависит от того, чтобы цикл выполнения был в определенном режиме - я не знаю, относится ли это к ситуации, когда запрос URLConnection запускается в tableView: cellForIndexPath.

Как можно отладить внутреннее устройство NSURLConnection?

[ОБНОВИТЬ]

Хорошо, поэтому я решил свой конкретный случай этой проблемы, явно запустив соединение в основном потоке. Я поместил код установки соединения в другой селектор и вызвал его через performSelectorOnMainThread, и все снова стало хорошо.

Вы должны попробовать зарегистрировать текущий режим цикла выполнения в селекторе, который запускает соединение - я обнаружил, что это не режим цикла выполнения по умолчанию, который требуется NSUrlConnection.

0
MostlyYes 26 Окт 2009 в 18:53

Я действительно потратил некоторое время на изучение кода.

В InternetImage я заметил, что есть эта функция. Что вам нужно сделать, так это заставить эту функцию сообщить вашему TableView "обновить". Вы можете сделать это, передав функцию делегата вашему классу, который вызывается позже, но теперь все становится очень сложно. Потому что, когда вы покидаете представление, вам нужно будет установить для функции делегата значение «nil» для каждого изображения, которое вы пытаетесь загрузить.

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

Отправьте ответ здесь, когда вы его получите, мне интересно узнать, решило ли это это или нет.

0
Jason Plank 21 Ноя 2011 в 20:40

Ну хороший подход? Первым делом проверьте, где вы размещаете изображение? 2-й, если ваше изображение фиксировано, означает конкретный, чем просто весь код в

if(cell == nil)
{

}

Сторона этого условия. Я столкнулся с такой же проблемой и решил вот так. дайте мне знать, работает это или нет.

0
Jason Plank 21 Ноя 2011 в 20:41