Почему изображения UItableViewCell не загружаются до прокрутки? - PullRequest
2 голосов
/ 08 августа 2009

Мой код загружает изображения асинхронно, используя 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;
    }

Ответы [ 7 ]

2 голосов
/ 08 августа 2009

Решение, которое сработало для меня, - это переместить ответственность за загрузку в 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;
}
1 голос
/ 13 августа 2009

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

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

1 голос
/ 08 августа 2009

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

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

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

0 голосов
/ 15 октября 2009

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

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

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

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

[ОБНОВЛЕНИЕ]

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

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

0 голосов
/ 19 августа 2009

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

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

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

Два быстрых побега

  1. InternetImage, кажется, делает много вещей, которые идиоматически несовместимы с общими соглашениями о программировании Какао. По крайней мере, для меня это затрудняет быстрое чтение, и я не совсем уверен, что все делает правильно.
  2. Не используйте _imageScaledToSize :. Это частный метод Apple, и если они заметят, что вы используете его, это может привести к отклонению.
0 голосов
/ 08 августа 2009

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

if(cell == nil)
{

}

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

0 голосов
/ 08 августа 2009

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

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

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

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

...