2016-06-27 5 views
14

У меня есть UITableView, который загружает изображения из службы. Он извлекает URL-адреса и загружает изображения с NSURLSession.sharedSession().dataTaskWithURL:UIImageView изначально не отображает изображение в UITableViewCell

NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, _, error) -> Void in 
          guard 
           let data = data where error == nil, 
           let image = UIImage(data: data) 
           else { 
            print(error?.localizedDescription) 
            return 
          } 
          dispatch_async(dispatch_get_main_queue()) { 
           [unowned self] in 
           self.pictureView.image = image 
           self.pictureView.layoutIfNeeded() 
          } 
         }).resume() 

UIImageView является выход на раскадровку сцены. Режим содержимого UIImageView - это Aspect Fit.

UIImageView для видимых ячеек первоначально отображается пустым (без изображения), хотя я подтвердил, что изображение было установлено. Пока камера не будет прокручена/на экране, ячейка будет перерисовываться (?) И отображать изображение.

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

self.pictureView.setNeedsDisplay() 
self.pictureView.setNeedsUpdateConstraints() 
self.pictureView.setNeedsLayout() 
self.pictureView.reloadInputViews() 

Ни один из них не решить эту проблему. Что вызывает отображение этого изображения только после прокрутки ячейки/просмотра?

EDIT: Возможно, важно отметить, что это происходит внутри наконечника и связанного с ним класса. Загружается ниб, затем я запускаю загрузку. Я задаюсь вопросом, выкопал ли ниб, и к моменту загрузки моего изображения и установки на UIImageView завершение макета завершено. Затем, перемещая ячейку (содержащую ниб) на экране, ячейка снова выполняет макет, и все отображается правильно. Просто догадаться, и я все еще придерживаюсь этого.

EDIT 2: Дальнейшее уточнение процесса:

1) Мои UIViewController содержит UITableView.

2) Этот UITableView загружает ячейки из пользовательского класса UITableViewCell под названием PostCell.

3) Все содержимое PostCell представляет собой одно представление, загруженное из ножа, называемого PostView (с классом PostView). Это происходит во время загрузки init:style:reuseIdentifier в PostCell:

postView = NSBundle.mainBundle().loadNibNamed("PostView", owner: self, options: nil).first as? PostView 

4) Затем я объект Сообщение от postView:

postView.post = Post 

Обратите внимание, что это происходит после awakeFromNib() вызывается PostView.

5) Свойство post на PostView имеет наблюдателя didSet, который запускает загрузку изображения на основе URL-адреса в объекте post (код выше). Изображение загружается и устанавливается на UIImageView по имени pictureView.

Edit 3:

Вот код для tableView(_:cellForRowAtIndexPath:):

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

    var cell = tableView.dequeueReusableCellWithIdentifier(postCellIdentifier) as? PostCell 

     if cell == nil { 
      cell = PostCell() 
     } 

     if let postCell = cell, 
      let postArray = postArray, 
      let post = postArray[indexPath.row] { 
      postCell.post = post 
      return postCell 
     } 

     // This section is hit during a refresh. 
     let emptyCell = UITableViewCell() 
     emptyCell.contentView.backgroundColor = .whiteColor() 
     return emptyCell 

    } 

Редактировать 4:

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

Таким образом, ограничения и размер UIImageView были установлены к тому моменту, когда изображение загружается, а снятие его/с экрана вызывает перерисовку, которая изменяет размер изображения должным образом. Если бы я мог программно запускать эту перерисовку, это помогло бы, но см. Выше для того, что я уже пробовал.

Редактировать 6:

Здесь находится в действии. Обратите внимание, что я установил изображение-заполнитель в наконечнике. Это, по-видимому, приводит к тому, что изображение будет отображаться при первоначальной загрузке, но по размерам заполнителя. При прокрутке его на экране кратковременное изменение размера, когда оно отображается правильно. enter image description here

Edit 7:

Я использую PostView перо в другом месте в моем приложении без проблем. Когда он загружается, размер изображения будет правильно установлен. Кажется, проблема только в том, что nib встроен в UITableViewCell.

Edit 8:

Я нашел причину этой проблемы, названную в viewDidLoad из моих UIViewController:

tableView.estimatedRowHeight = 500 
tableView.rowHeight = UITableViewAutomaticDimension 

Использования автоматического измерения вызывает первоначальную неправильную высоту строки. По какой-то причине прокрутка ячейки/в представлении будет правильно подсчитать высоту строки, и изображение будет отображаться на правильной высоте. Удаление этого кода приведет к отображению изображения, но высота строки не может быть использована для расчета автоматической высоты. Каков наилучший метод автоматического расчета высоты строк и все еще избежать этой проблемы?

+0

когда вы называете dataTaskWithURL? –

+0

@TejaNandamuri Он вызывается в 'tableView: cellForRowAtIndexPath'. Я получаю пользовательский 'UITableViewCell' с' dequeueReusableCellWithIdentifier'. Затем я устанавливаю объект на эту ячейку, у которой есть 'didSet', который извлекает изображение. – Fook

+0

'self.pictureView.image = image' или' cell.pictureView.image = image'? – Proton

ответ

2

После удаления ячейки для просмотра таблицы, вероятно, различной ширины вид ячейки и стола. В этот момент ваша ячейка еще не будет автоматически автоматически изменена. Так изменить этот кусок кода после того, как из извлечения из табличных:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    ... 
    if let postCell = cell, 
     let postArray = postArray, 
     let post = postArray[indexPath.row] { 

      // set the post with image after the cell is sized correctly 
      dispatch_async(dispatch_get_main_queue()) { 
       postCell.post = post 
      } 
     return postCell 
    } 

Убедитесь, что вы правильно настройки ограничений в файле ячейка бобов.

Я предлагаю также использовать метод registerNib внутри метода контроллера viewDidLoad:

tableView?.registerNib(UINib(nibName: "PostCellName", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: postCellIdentifier) 
+0

Установка 'post' в главной очереди не помогла, однако я смог найти основную причину. Пожалуйста, см. Редактировать 8 моего сообщения и, пожалуйста, ответьте, если у вас есть какие-либо советы по его устранению. Благодарю. – Fook

4

вы можете попробовать это расширение:

extension UIImageView { 
    func downloadedFrom(link link:String, contentMode mode: UIViewContentMode) { 
     guard 
      let url = NSURL(string: link) 
      else {return} 
     contentMode = mode 
     NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in 
      guard 
       let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200, 
       let mimeType = response?.MIMEType where mimeType.hasPrefix("image"), 
       let data = data where error == nil, 
       let image = UIImage(data: data) 
       else { return } 
      dispatch_async(dispatch_get_main_queue()) {() -> Void in 
       self.image = image 
      } 
     }).resume() 
    } 
} 

И тогда вы можете поместить эту строку, где вы хотите

cell.cellPhoto.downloadedFrom(link: "customImageUrl", contentMode: .ScaleAspectFit) //Try changing contentMode 
+0

Проблема, похоже, 'UITableViewAutomaticDimension'. См. Редактировать 8 в моем вопросе. – Fook

+0

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

+0

Хороший помощник! У меня только одна проблема. Если изображение велико, оно не загружает его, и оно показывает пустое изображение. –

1

После завершения загрузки вам необходимо перезагрузить данные таблицы. попробуйте позвонить self.tableview.reloadData() внутри dispatch_async блока

А также, если вы хотите, чтобы загрузить изображение из URL асинхронно Я рекомендую использовать Kingfisher

+0

Я пробовал это, но это не помогло. Проблема, похоже, 'UITableViewAutomaticDimension'. См. Редактировать 8 в моем вопросе. – Fook

2

Вы видели WWDC 2014 Session 226? Возможно, видео может пролить свет на вашу проблему.

В сессии WWDC они используют self.tableView.estimatedRowHeight = 100.0;, которые вы сделали ✅, а потом они говорят, что вы после этого не нужны какое-либо фиктивной ячейки прототипа из контроллера представления таблицы.

Проблема заключается в том, что когда раскадровка загружает представление таблицы, она устанавливает свойство rowHeight в значение из построителя xib/interface. Чтобы использовать автоматические ячейки, вам необходимо установить estimatedRowHeight и оставить rowHeight тем, что должно быть по умолчанию - UITableViewAutomaticDimension. (, которые вы сделали) ✅

self.tableView.rowHeight = UITableViewAutomaticDimension

⚠️The новая проблема заключается в том, что первые клетки нагрузки, прежде чем вы добросовестный высоту строки. Трюк, возможно, заставит перезагрузить таблицу, когда появится представление?

[super viewDidAppear:animated] 
[self.tableView reloadData] 

Если это не сработает, то, возможно, вы случайно установить явную contentWidth/Рост на то, что является причиной не быть размером properly❓❓

0

Посмотрев на процессе редактирования (8 в настоящее время) и обновления от ваших респондентов, похоже, что вы используете ту же ошибку в методе init, которая вызвала errors in init mentioned here.

Кредит следует уделять Prcela быть на правильном пути с регистрации перо, но более элегантное решение приводится ниже:

Самый простой способ (доступна с прошивкой 5.0), чтобы создать вид пользовательских таблиц ячейка в файле nib должна использовать registerNib: forCellReuseIdentifier: в контроллере табличного представления. Большим преимуществом является то, что dequeueReusableCellWithIdentifier: затем автоматически создает экземпляр ячейки из файла nib, если это необходимо. Вам больше не понадобится if (cell == nil) ... part.

В таблице View Controller под viewDidLoad

[self.tableView registerNib:[UINib nibWithNibName:@"CueTableCell" bundle:nil] forCellReuseIdentifier:@"CueTableCell"]; 

В cellForRowAtIndexPath добавить

CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CueTableCell"]; 
// setup cell 
return cell; 

клетки, нагруженные из файла СИБ инстанцируются с помощью initWithCoder, вы можете переопределить в вашем подкласс, если необходимо. Для модификаций элементов пользовательского интерфейса вы должны переопределить awakeFromNib (не забудьте указать вызов «супер»).

Желаю получить кредит на такое элегантное решение, но этот кредит принадлежит Martin R с того же поста.

+0

Проблема в том, что обновление изображения внутри пользовательской ячейки не инициирует сначала обновление макета. «UIImageView» не обновляется до тех пор, пока он не будет снят с экрана, а затем на экране, после чего произойдет перерисовка (?) И изображение будет отображаться. Если я задаю образ заполнителя на 'UIImageView', загруженное изображение отображается с высотой изображения заполнителя и изменяется только после того, как оно выходит за пределы экрана, а затем на экране. Решение этой проблемы будет заключаться в том, как инициировать это изменение размера программно после загрузки. – Fook

Смежные вопросы