2009-08-08 2 views
2

Мой код загружает изображения асинхронно с использованием InternetImage в методе tableview: cellForRowAtIndexPath:, инициализируя IntentImage с помощью initWithURL и вызывая downloadImage. Этот код отлично работает после прокрутки до новой ячейки UITableViewCell в UITableView, но не раньше, хотя URL-адрес правильный в обоих случаях. Никто из методов делегирования NSURLConnection InternetImage не вызван, чтобы уведомить меня об успешном или неудачном подключении, как и должно быть. Вызов reloadData:, setNeedsDisplay: и setNeedsLayout: ничего не предпринимают, поскольку изображение не загружается.Почему загрузка изображений в формате UItableViewCell пока не прокручивается?

Вот код из моего подкласса 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

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

Такой подход потребует следующие изменения:

  • Переместить ответственность за загрузку изображения на объект.
  • Изменить объект для отправки события/вызова делегата/уведомления о том, что свойство изменилось при изменении изображения.
  • Подкласс 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; 
} 
+0

Хм, мне нравится это решение, оно очень тщательное и спасибо за образец кода. Тем не менее, я не обязательно смотрю, как переписать мой способ загрузки изображений, я доволен выполнением текущего способа после прокрутки. Если нет более простого решения, мне, возможно, придется это сделать, но, похоже, очень много работы, чтобы загрузить изображения на первое представление. –

1

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

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

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

+0

Моя проблема несколько отличается от этой. Загрузка изображений с помощью tableView: cellForRowAtIndexPath: работает так, как нужно, как только я прокручиваю. tableView: cellForRowAtIndexPath: вызывается для ячеек, которые первоначально отображаются на экране, и он правильно создает объект InternetImage для каждого с правильной ссылкой, но по какой-то причине загрузка не выполняется. Однако методы NSURLConnection не регистрируют успех или ошибку при загрузке исходных ячеек, которые появляются на экране. Я пытаюсь исправить эту ошибку, а при загрузке изображений с помощью tableView: cellForRowAtIndexPath :. –

+0

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

0

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

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

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

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

+0

Мы уже реализуем эту функцию и теперь вызываем [tableView reloadData], и она отлично работает для всех ячеек, как только я прокручиваю. Проблема, однако, в том, что начальные ячейки на экране, когда они создают экземпляр InternetImages, не доходят до этого метода и не сообщают о ошибке загрузки. У меня есть записи журнала по всему этому коду, и для жизни я не могу понять, почему NSURLConnection не сообщает ни об успешности, ни об ошибке для загрузки изображения. Может быть, это проблема с NSURLConnection? –

+0

Я просто хочу добавить, что InternetImage будет правильно сообщать о достижениях и ошибках NSURLConnection, как только я прокручу, только не изначально. –

0

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

if(cell == nil) 
{ 

} 

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

1

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

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

+0

Я понимаю это, но проблема в том, что класс оболочки NSURLConnection не получает данные ПЕРЕД прокруткой. После прокрутки в любом месте загрузка изображений работает правильно с этой точки вперед. –

+0

Вы можете определенно запустить NSURLConnections во время прокрутки, я делаю это все время. В 2.0 (и 2.1, если я правильно помню) вы не могли обновить ячейку во время ее перемещения, потому что UITableView сделал снимок, прежде чем он прокручивал и использовал это, чтобы увеличить частоту кадров прокрутки, но теперь вы определенно можете это сделать. –

0

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

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

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

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

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

Мы внесли одну единственную модификацию в InternetImage для хранения объекта, который не имеет отношения к остальной части кода, и при удалении такая же проблема сохраняется. Код для internetImageReady не имеет значения, если он никогда не вызывается, и ни один из методов делегата NSURLConnection, присутствующих в InternetImage, не вызывается. Что происходит в правильном случае, так это то, что все работает отлично, изображение загружается и отображается в перезагруженном tableView. Итак, как я точно знаю, что нужно для «заправки насоса»? Я посмотрю, как вручную получить runloop и отчитаться. –

+0

С помощниками - InternetImage в порядке, это просто класс оболочки для NSURLConnection и ничем не отличается от ручного создания и запуска его, кроме предоставления удобства.Кроме того, мы уже изменили код _imageScaledToSize: и никогда не собирались его хранить, мы больше заботились о том, чтобы устранить эту проблему. Спасибо за ваше время на этот ответ. –

0

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

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

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

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

[UPDATE]

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

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