TL;DR: высота, вычисляемая механизмом автомакета при отображении ячейки, содержащей представление коллекции, всегда неверна в первый раз. Вызов reloadData()
в табличном представлении устраняет проблему, но также делает табличное представление скачком и становится непригодным для использования. Есть идеи?
Пояснения. У меня есть табличное представление с множеством разных ячеек разной высоты.
Одна из этих ячеек представляет собой галерею изображений, которая может содержать одно или несколько изображений, загруженных асинхронно.
Ограничения у меня следующие:
- если галерея содержит только одно изображение и если это изображение ориентировано в альбомной ориентации, высота галереи должна быть равна высоте изображения
- в противном случае галерея имеет фиксированную высоту.
Проблемы, с которыми я сталкиваюсь, следующие:
Я получаю проблему Super Annoying Encapsulated-Height-Layout, когда табличное представление пытается отобразить ячейку галереи в первый раз. Эта инкапсулированная высота всегда имеет неправильное значение, даже несмотря на то, что ограничение высоты в представлении коллекции было обновлено.
Табличное представление никогда не получает правильный размер ячейки с первой попытки.
Единственный способ, которым я могу заставить табличное представление правильно отображать ячейку, - это когда я вызываю 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()
}
}
}
Вот как я устанавливаю свой представление коллекции:
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(_:)
, но это ничего не меняет.
Извините за большой кусок кода, но это сводит меня с ума.
Encapsulated-Height-Layout
== высота строки в раскадровке? - person Ismail   schedule 27.01.2016Encapsulated-Height-Layout
равно 230, что является высотой представления коллекции по умолчанию (200) + интервал по вертикали (30). - person Performat   schedule 27.01.2016