2016-01-27 18 views
6

TL; DR: l'altezza calcolata dal motore di autolayout quando si visualizza la cella contenente la vista di raccolta è sempre errata la prima volta. Chiamando il numero reloadData() sulla vista tabella si corregge il problema, ma si fa anche saltare la vista tabella e diventare inutilizzabile. Qualche idea?Utilizzo del layout automatico in UITableViewCell contenente UICollectionView con caricamento di immagini asincrone

Spiegazioni: Ho una vista tabella con molte celle diverse di altezze diverse.

Una di queste celle è una galleria di immagini che può contenere una o più immagini caricate in modo asincrono.

I vincoli che ho sono i seguenti:

  • se la galleria contiene una sola immagine, e se questa immagine è orientata nel paesaggio, l'altezza della galleria dovrebbe essere l'altezza dell'immagine
  • altro , la galleria ha un'altezza fissa.

I problemi che devo affrontare sono le seguenti:

  • ottengo il Super fastidioso problema cosa incapsulato-altezza-Layout quando la vista tabella tenta di visualizzare la cellula galleria la prima volta. Questa altezza incapsulata ha sempre il valore sbagliato, anche se il vincolo di altezza nella vista raccolta è stato aggiornato.

  • La vista tabella non ottiene mai le dimensioni della cella al primo tentativo.

    • Anche se l'immagine è già recuperata quando viene visualizzata la cellula, i display delle cellule male e devo scorrere su/giù per nasconderlo, quindi visualizzare di nuovo per ottenere la giusta dimensione ... fino alla prossima volta la dimensione della cella deve essere nuovamente calcolata. Vedi sotto: enter image description here
  • L'unico modo che posso ottenere per forzare la vista tabella per visualizzare correttamente la cella è quando chiamo reloadData sul tavolo vista una volta che l'immagine viene caricata la prima volta ... che fa la vista tabella per saltare e per essere praticamente inutilizzabile.

sto usando Kingfisher per recuperare le immagini, ecco il codice:

UICollectionViewCell Fonte:

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 
} 

Ecco cosa succede quando il delegato viene chiamato sul RooViewController in 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() 
     } 
    } 
} 

Ecco come ho impostato la mia Collezione Vista:

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 
}} 

e la cella personalizzato tenendolo:

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") 
} } 

Ultimo ma non meno importante, ho impostato il vincolo altezza del vista raccolta in tableView(_:willDisplayCell:forRowAtIndexPath:)

Ho provato a impostarlo anche in cellForRowAtIndexPath(_:) ma non cambia nulla.

Ci scusiamo per il gran numero di codice, ma questo mi sta facendo impazzire.

+0

è il valore 'Encapsulated-Height-Layout == the rowheight nello storyboard? – Ismail

+0

Il 'Encapsulated-Height-Layout' è uguale a 230, che è l'altezza predefinita della vista di raccolta (200) + la spaziatura verticale (30) – Performat

+0

Quando viene visualizzato l'avviso. ti dice che ha dovuto rompere uno dei tuoi vincoli per sistemarlo. Probabilmente, (il vincolo di altezza) per correggere questo avvertimento basta rendere la priorità 999 di quel vincolo (se era 1000). – Ismail

risposta

0

Quando viene visualizzato questo avviso. ti dice che ha dovuto rompere uno dei tuoi vincoli per sistemarlo. Probabilmente, (il vincolo di altezza) per correggere questo avvertimento basta rendere la priorità 999 di quel vincolo (se era 1000).


Per aggiornare la cella dopo aver impostato l'immagine su di essa. vuoi provare questo:

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) 
         } 

e rimuovere tableView.reloadData()

favore mi invia un feedback dopo lo provate.

+0

Ho impostato il layout della cella utilizzando tre serie di vincoli. Uno orizzontale, uno verticale (entrambi che utilizzano vfl) e l'ultimo è un vincolo singolo dedicato all'altezza della vista dell'insieme. L'avvertimento dice che il sistema ha recuperato interrompendo il set verticale di vincoli: 'Tentativo di ripristino interrompendo il vincolo ' – Performat

+0

Hai provato ad applicare la mia modifica al tuo codice? – Ismail

+0

Ho provato a chiamare 'setNeedsLayout()' e 'cell.layoutifNeeded()' sulla cella e purtroppo non è stato d'aiuto. Per quanto riguarda i vincoli, ho abbassato la priorità del vincolo di altezza della vista di raccolta, ma non è stato di alcun aiuto. Come suggerito dal messaggio di debug, penso che sia perché non è il vincolo che sta effettivamente generando l'avviso. Ma non sono sicuro di come dovrei abbassare la priorità di tutti i vincoli verticali ... Proverò ora ad impostarli individualmente e ad abbassarne la priorità. – Performat

Problemi correlati