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:
- 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:
- 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.
è il valore 'Encapsulated-Height-Layout == the rowheight nello storyboard? – Ismail
Il 'Encapsulated-Height-Layout' è uguale a 230, che è l'altezza predefinita della vista di raccolta (200) + la spaziatura verticale (30) – Performat
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