2016-01-27 1 views
6

TL; DR: Высота, вычисленная механизмом автоотключения при отображении ячейки, содержащей просмотр коллекции, всегда ошибочна в первый раз. Вызов reloadData() в представлении таблицы устраняет проблему, но также приводит к переходу таблицы и ее непригодности. Есть идеи?Использование автоматической компоновки в UITableViewCell, содержащей UICollectionView с нагрузкой асинхронного изображения

Пояснения: У меня есть табличный вид со множеством разных ячеек разной высоты.

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

Ограничения У меня есть следующие:

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

Проблемы я сталкиваюсь являются следующими:

  • я получаю супер досадную Герметичную высоты-макет выпуска вещи, когда представление таблицы пытается отобразить в галерее клетки в первый раз. Эта инкапсулированная высота всегда имеет неправильное значение, даже если ограничение высоты в представлении коллекции обновлено.

  • Вид таблицы последовательно не получает размер ячейки сразу при первой попытке.

    • Даже если образ уже получен, когда клетка отображается, в ячейке отображается плохо, и я не придется прокручивать вверх/вниз, чтобы скрыть его, а затем отобразить его снова, чтобы получить правильный размер ... до следующего раза размер ячейки должен быть вычислен снова. Смотрите ниже: enter image description here
  • Единственный способ, которым я могу получить, чтобы заставить вид таблицы, чтобы правильно отобразить ячейку, когда я называю reloadData на табличном после того, как изображение загружается в первый раз ... что делает табличный вид, чтобы прыгать и быть в основном непригодным для использования.

Я использую Kingfisher, чтобы получить изображения, вот код:

UICollectionViewCell Источник данных:

func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 

    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CarouselCollectionViewCell", forIndexPath: indexPath) as! CarouselCollectionViewCell 
    guard let collectionView = collectionView as? CarouselCollectionView else { return cell } 

    if let imagesURLs = data.imagesURLs { 
     let url = imagesURLs[indexPath.item] 

     if let smallURL = url.small { 
      KingfisherManager.sharedManager.retrieveImageWithURL(
       smallURL, 
       optionsInfo: KingfisherOptionsInfo(), 
       progressBlock: nil, 
       completionHandler: { (image, error, cacheType, imageURL) ->() in 
        if let image = image { 
        self.delegate?.imageIsReadyForCellAtIndexPath(image, collectionView: collectionView, screenshotIndex: indexPath.row) 
        cell.imageView.image = image 
        } 
      }) 
     } 
    } 
    return cell 
} 

Вот что происходит, когда делегат вызывается на RooViewController в imageIsReadyForCellAtIndexPath(image: UIImage, collectionView: UICollectionView, screenshotIndex: Int):

func imageIsReadyForCellAtIndexPath(image: UIImage, collectionView: UICollectionView, screenshotIndex: Int) { 
    guard let collectionView = collectionView as? CarouselCollectionView else { return } 
    guard let collectionViewIndexPath = collectionView.indexPath else { return } 
    guard let screenshotsCount = feed?.articles?[collectionViewIndexPath.section].content?[collectionViewIndexPath.row].data?.imagesURLs?.count else { return } 

    let key = self.cachedSizesIndexPath(collectionViewIndexPath: collectionViewIndexPath, cellIndexPath: NSIndexPath(forItem: screenshotIndex, inSection: 0)) 
    var sizeToCache: CGSize! 

    if screenshotsCount == 1 { 

     // Resize the collectionView to fit a landscape image: 
     if image.isOrientedInLandscape { 
      sizeToCache = image.scaleToMaxWidthAndMaxHeight(maxWidth: Constants.maxImageWidth, maxHeight: Constants.maxImageHeight) 
     } else { 
      sizeToCache = image.scaleToHeight(Constants.maxImageHeight) 
     } 

     if collectionViewCellsCachedSizesObject.dict[key] == nil { 

      let flowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout 
      let imageWidth = sizeToCache.width 
      let sidesInset = (collectionView.frame.width - imageWidth)/2 
      print("sidesInset: ", sidesInset) 
      flowLayout.sectionInset = UIEdgeInsets(top: 0, left: sidesInset, bottom: 0, right: sidesInset) 

      collectionViewCellsCachedSizesObject.dict[key] = sizeToCache 
      collectionView.heightConstraint.constant = sizeToCache.height 
      collectionView.collectionViewLayout.invalidateLayout() 
      collectionView.setNeedsUpdateConstraints() 

      tableView.reloadData() 
     } 
    } else { 

     let sizeToCache = image.scaleToHeight(Constants.maxImageHeight) 

     if collectionViewCellsCachedSizesObject.dict[key] == nil { // && collectionViewCellsCachedSizesObject.dict[key] != sizeToCache { 
      collectionViewCellsCachedSizesObject.dict[key] = sizeToCache 
      collectionView.collectionViewLayout.invalidateLayout() 
     } 
    } 
} 

Вот как я могу установить Collection View:

class CarouselElement: Element { 

let collectionView: CarouselCollectionView 

func cachedSizesIndexPath(collectionViewIndexPath aCollectionViewIndexPath: NSIndexPath, cellIndexPath aCellIndexPath: NSIndexPath) -> String { 
    return "\(aCollectionViewIndexPath), \(aCellIndexPath)" 
} 

override init(style: UITableViewCellStyle, reuseIdentifier: String?) { 

    let layout = UICollectionViewFlowLayout() 
    layout.sectionInset = UIEdgeInsets(top: 0, left: Constants.horizontalPadding, bottom: 0, right: Constants.horizontalPadding) 
    layout.scrollDirection = .Horizontal 

    collectionView = CarouselCollectionView(frame: CGRectZero, collectionViewLayout: layout) 
    collectionView.translatesAutoresizingMaskIntoConstraints = false 

    collectionView.registerClass(CarouselCollectionViewCell.self, forCellWithReuseIdentifier: "CarouselCollectionViewCell") 
    collectionView.allowsMultipleSelection = false 
    collectionView.allowsSelection = true 
    collectionView.backgroundColor = Constants.backgroundColor 
    collectionView.showsHorizontalScrollIndicator = false 

    super.init(style: style, reuseIdentifier: reuseIdentifier) 

    addSubview(collectionView) 

    addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
     "|[collectionView]|", 
     options: NSLayoutFormatOptions(), 
     metrics: nil, 
     views: ["collectionView":collectionView])) 

    addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
     "V:|[collectionView]-verticalPadding-|", 
     options: NSLayoutFormatOptions(), 
     metrics: ["verticalPadding":Constants.verticalPadding], 
     views: ["collectionView":collectionView])) 

    collectionView.heightConstraint = NSLayoutConstraint(
     item: collectionView, 
     attribute: .Height, 
     relatedBy: .Equal, 
     toItem: nil, 
     attribute: .NotAnAttribute, 
     multiplier: 1.0, 
     constant: 200) 
    addConstraint(collectionView.heightConstraint) 
} 

required init?(coder aDecoder: NSCoder) { 
    fatalError("init(coder:) has not been implemented") 
} 

func setCarouselDataSourceDelegate(dataSourceDelegate: CarouselDataSourceDelegate?, indexPath: NSIndexPath, cachedHeight: CGFloat?) { 
    collectionView.indexPath = indexPath 
    if let height = cachedHeight { 
     collectionView.heightConstraint.constant = height 
    } 
    collectionView.dataSource = dataSourceDelegate 
    collectionView.delegate = dataSourceDelegate 
    collectionView.reloadData() 
} 

override func prepareForReuse() { 
    super.prepareForReuse() 
    collectionView.contentOffset = CGPointZero 
}} 

И Пользовательские сотовый держа его:

class CarouselCollectionViewCell: UICollectionViewCell { 

let imageView: UIImageView 

override init(frame: CGRect) { 

    imageView = UIImageView.autolayoutView() as! UIImageView 
    imageView.image = Constants.placeholderImage 
    imageView.contentMode = .ScaleAspectFit 

    super.init(frame: frame) 

    translatesAutoresizingMaskIntoConstraints = false 

    addSubview(imageView) 

    addConstraints(
     NSLayoutConstraint.constraintsWithVisualFormat(
      "|[imageView]|", 
      options: NSLayoutFormatOptions(), 
      metrics: nil, 
      views: ["imageView":imageView])) 
    addConstraints(
     NSLayoutConstraint.constraintsWithVisualFormat(
      "V:|[imageView]|", 
      options: NSLayoutFormatOptions(), 
      metrics: nil, 
      views: ["imageView":imageView])) 
} 

override func prepareForReuse() { 
    imageView.image = Constants.placeholderImage 
} 

required init?(coder aDecoder: NSCoder) { 
    fatalError("init(coder:) has not been implemented") 
} } 

И последнее, но не в последнюю очередь, я установил ограничение высоты вид сбора в tableView(_:willDisplayCell:forRowAtIndexPath:)

Я попытался установить его также в cellForRowAtIndexPath(_:) , но это ничего не меняет.

Извините за массивный кусок кода, но это сводит меня с ума.

+0

- значение «Encapsulated-Height-Layout» == rowheight в раскадровке? – Ismail

+0

«Инкапсулированная высота-макет» равна 230, которая является стандартной высотой представления коллекции (200) + вертикальным интервалом (30) – Performat

+0

Когда появится это предупреждение. скажите, что вам пришлось сломать одно из ваших ограничений, чтобы исправить это. Вероятно, (ограничение высоты), чтобы исправить это предупреждение, просто назначьте приоритет этого ограничения 999 (если он был 1000). – Ismail

ответ

0

При появлении этого предупреждения. скажите, что вам пришлось сломать одно из ваших ограничений, чтобы исправить это. Вероятно, (ограничение высоты), чтобы исправить это предупреждение, просто назначьте приоритет этого ограничения 999 (если он был 1000).

Для обновления ячейки после установки изображения на нее. Вы бы попробовать это:

dispatch_async(dispatch_get_main_queue()) {() -> Void in 
          self.delegate?.imageIsReadyForCellAtIndexPath(image, collectionView: collectionView, screenshotIndex: indexPath.row) 
          cell.imageView.image = image 
          // get the content offset. 
          let offset = self.tableView.contentOffset 
          let visibleRect = CGRect(origin: offset, size: CGSize(width: 1, height: 1) 
          // reload the cell 
          tableView.beginUpdates() 
          tableView.endUpdates() 
          // to prevent the jumping you may scroll to the same visible rect back without animation. 
          self.tableView.scrollRectToVisible(visibleRect, animated: false) 
         } 

и удалить tableView.reloadData()

Пожалуйста, дайте мне обратную связь после того, как вы попробуете его.

+0

Я установил компоновку ячейки, используя три набора ограничений. Один горизонтальный, один вертикальный (как с использованием vfl), так и последний - это одно ограничение, предназначенное для высоты представления коллекции. Предупреждение говорит, что система восстановилась, нарушив вертикальный набор ограничений: 'попытается восстановить с помощью ограничения на разрыв ' – Performat

+0

Вы пытались применить мои изменения к вашему коду? – Ismail

+0

Я попытался вызвать 'setNeedsLayout()' и 'cell.layoutifNeeded()' в ячейке, и это не помогло, к сожалению. Что касается ограничений, я уменьшил приоритет ограничения высоты представления коллекции, но это тоже не помогло. Как было намечено отладочным сообщением, я думаю, что это потому, что это не ограничение, которое фактически порождает предупреждение. Но не уверен, как я должен снизить приоритет всех вертикальных ограничений ... Я попробую теперь настроить их индивидуально и понизить их приоритет. – Performat

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