TL; DR :第一次显示包含集合视图的单元格时,autolayout引擎计算的高度总是错误的。在表视图上调用reloadData()
可以解决问题,但也会使表视图跳转并无法使用。有什么想法吗?
说明: 我有一个表视图,其中包含许多不同高度的不同单元格。
其中一个单元格是一个图像库,可以包含一个或多个异步加载的图像。
我的约束如下:
我面临的问题如下:
当表格视图第一次尝试显示图库单元格时,我收到了Super Annoying Encapsulated-Height-Layout问题。即使已更新集合视图上的高度约束,此封装高度始终是错误的值。
表格视图在第一次尝试时始终无法正确获取单元格的大小。
我正在使用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
}
以下是在imageIsReadyForCellAtIndexPath(image: UIImage, collectionView: UICollectionView, screenshotIndex: Int)
中 RooViewController 上调用委托时会发生的情况:
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(_:)
中设置它,但它不会改变任何内容。
对于巨大的代码块感到抱歉,但这让我感到疯狂。
答案 0 :(得分: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()
请在尝试后给我反馈。