UIImageView вначале не отображает изображение в UITableViewCell
У меня есть UITableView
, который загружает изображения из службы. Он извлекает URL-адреса и загружает изображения с помощью NSURLSession.sharedSession().dataTaskWithURL
:
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
завершение макета завершено. Затем, перемещая ячейку (содержащую ниб) на экране, ячейка снова выполняет макет, и все отображается правильно. Просто догадаться, и я все еще придерживаюсь этого.
ИЗМЕНИТЬ 2:
Дальнейшее уточнение процесса:
1) Мой UIViewController
содержит a 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) Затем я устанавливаю объект Post на postView
:
postView.post = Post
Обратите внимание, что это происходит ПОСЛЕ awakeFromNib()
вызывается в PostView.
5) Свойство post
в PostView имеет наблюдателя didSet
, который запускает загрузку изображения на основе URL-адреса в объекте post
(код выше). Изображение загружается и устанавливается на UIImageView
с именем pictureView
.
Изменить 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
, предварительно установив изображение-заполнитель в nib, загрузите его изображение в UIImageView
, изображение будет отображаться на высоте заполнителя. Когда я снова вытащу эту ячейку с экрана, затем UIImageView
будет рисовать изображение с правильным размером, используя установку ограничений в nib.
Таким образом, кажется, что ограничения и размер UIImageView
были установлены к тому времени, когда загружается изображение, и вытаскивание его/на экране вызывает перерисовку, которая изменяет размер изображения должным образом. Если бы я мог программно запускать эту перерисовку, это помогло бы, но см. Выше для того, что я уже пробовал.
Изменить 6:
Здесь он находится в действии. Обратите внимание, что я установил образ заставки в наконечник. Это, по-видимому, приводит к тому, что изображение будет отображаться при первоначальной загрузке, но по размерам заполнителя. При прокрутке его на экране кратковременное изменение размера, когда оно отображается правильно.
![введите описание изображения здесь]()
Изменить 7:
Я использую PostView nib в другом месте в своем приложении без проблем. Когда он загружается, размер изображения будет правильно установлен. Кажется, проблема только в том, что nib встроен в UITableViewCell
.
Изменить 8:
Я нашел основную причину этой проблемы, вызванную в viewDidLoad моего UIViewController
:
tableView.estimatedRowHeight = 500
tableView.rowHeight = UITableViewAutomaticDimension
Использование автоматического измерения приводит к начальной неправильной высоте строки. По какой-то причине прокрутка ячейки/в представлении будет правильно подсчитать высоту строки, и изображение будет отображаться на правильной высоте. Удаление этого кода приведет к отображению изображения, но высота строки не может быть использована для расчета автоматической высоты. Каков наилучший способ автоматического вычисления высоты строк и все еще избежать этой проблемы?
Ответы
Ответ 1
вы можете попробовать это расширение:
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
Ответ 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
}
Убедитесь, что вы правильно настроили ограничения в файле nib ячейки.
Я предлагаю также использовать метод registerNib внутри метода viewDidLoad контроллера:
tableView?.registerNib(UINib(nibName: "PostCellName", bundle: NSBundle.mainBundle()), forCellReuseIdentifier: postCellIdentifier)
Ответ 3
Вы видели WWDC 2014 Session 226? Возможно, видео может пролить свет на вашу проблему.
В сеансе WWDC они используют self.tableView.estimatedRowHeight = 100.0;
, который вы сделали ✅, и затем они говорят, что вам не нужна какая-либо фиктивная ячейка прототипа из контроллера табличного представления.
Проблема тогда в том, что когда раскадровка загружает представление таблицы, она устанавливает свойство rowHeight
в значение из построителя xib/interface. Чтобы использовать ячейки авторазмера, вам нужно установить estimatedRowHeight
и оставить rowHeight
тем, что должно быть по умолчанию - UITableViewAutomaticDimension
. (, который вы сделали) ✅
self.tableView.rowHeight = UITableViewAutomaticDimension
⚠️ Новая проблема заключается в том, что первые ячейки загружаются до того, как у вас будет истинная высота строки. Возможно, трюк может заставить перезагрузить таблицу, когда отображается представление?
[super viewDidAppear:animated]
[self.tableView reloadData]
Если это не сработает, возможно, вы случайно настроили явный contentWidth/Height на то, что заставляет его не иметь нужного размера.
Ответ 4
После завершения загрузки вам необходимо перезагрузить данные таблицы. попробуйте вызвать self.tableview.reloadData()
внутри dispatch_async
block
А также, если вы хотите загрузить изображение из URL асинхронно, я рекомендую использовать Kingfisher
Ответ 5
После просмотра курса редактирования (8 в настоящее время) и обновлений от ваших респондентов кажется, что вы используете ту же ошибку в методе init, которая вызвала ошибки в init, упомянутый здесь.
Кредит должен быть предоставлен Prcela на правильном пути с регистрацией наконечника, но более элегантное решение представлено ниже:
Самый простой способ (доступный с iOS 5.0) создать пользовательскую ячейку таблицы в файле nib - использовать registerNib: forCellReuseIdentifier: в контроллере табличного представления. Большим преимуществом является то, что dequeueReusableCellWithIdentifier: затем автоматически создает экземпляр ячейки из файла nib, если это необходимо. Вам больше не понадобится if (cell == nil)... part.
В контроллере табличного представления в разделе viewDidLoad
[self.tableView registerNib:[UINib nibWithNibName:@"CueTableCell" bundle:nil] forCellReuseIdentifier:@"CueTableCell"];
В cellForRowAtIndexPath
добавить
CueTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CueTableCell"];
// setup cell
return cell;
Ячейки, загруженные из файла nib, создаются с использованием initWithCoder, вы может переопределить это в вашем подклассе, если это необходимо. Для внесения изменений в элементы пользовательского интерфейса, вы должны переопределить awakeFromNib (не забудьте вызов "супер" ).
Жаль, что я не мог взять кредит на такое изящное решение, но этот кредит принадлежит Martin R из той же записи.