Подтвердить что ты не робот

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

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

4b9b3361

Ответ 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 из той же записи.