UICollectionViewLayout is not working properly

I followed this tutorial on building custom collections from raywenderlich.com using xcode 8 and swift 3.

When I launched the application after implementing all the methods requested in the tutorial, I received the following error:

'no UICollectionViewLayoutAttributes instance for -layoutAttributesForItemAtIndexPath: {length = 2, path = 0 - 11}'

So, I added the following class to the CollectionViewFlowLayout class:

override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { return self.cache[indexPath.row] } 

This almost works, except that some cells overlap existing cells as they scroll down and then disappear. If I scroll up, everything will work fine.

I donโ€™t understand the full logic of this code, but I tested and tested it several times, and I canโ€™t understand which part of the code causes this behavior.

Any idea?

 import UIKit /* The heights are declared as constants outside of the class so they can be easily referenced elsewhere */ struct UltravisualLayoutConstants { struct Cell { /* The height of the non-featured cell */ static let standardHeight: CGFloat = 100 /* The height of the first visible cell */ static let featuredHeight: CGFloat = 280 } } class UltravisualLayout: UICollectionViewLayout { // MARK: Properties and Variables /* The amount the user needs to scroll before the featured cell changes */ let dragOffset: CGFloat = 180.0 var cache = [UICollectionViewLayoutAttributes]() /* Returns the item index of the currently featured cell */ var featuredItemIndex: Int { get { /* Use max to make sure the featureItemIndex is never < 0 */ return max(0, Int(collectionView!.contentOffset.y / dragOffset)) } } /* Returns a value between 0 and 1 that represents how close the next cell is to becoming the featured cell */ var nextItemPercentageOffset: CGFloat { get { return (collectionView!.contentOffset.y / dragOffset) - CGFloat(featuredItemIndex) } } /* Returns the width of the collection view */ var width: CGFloat { get { return collectionView!.bounds.width } } /* Returns the height of the collection view */ var height: CGFloat { get { return collectionView!.bounds.height } } /* Returns the number of items in the collection view */ var numberOfItems: Int { get { return collectionView!.numberOfItems(inSection: 0) } } // MARK: UICollectionViewLayout /* Return the size of all the content in the collection view */ override var collectionViewContentSize : CGSize { let contentHeight = (CGFloat(numberOfItems) * dragOffset) + (height - dragOffset) return CGSize(width: width, height: contentHeight) } override func prepare() { let standardHeight = UltravisualLayoutConstants.Cell.standardHeight let featuredHeight = UltravisualLayoutConstants.Cell.featuredHeight var frame = CGRect.zero var y:CGFloat = 0 for item in 0 ... (numberOfItems - 1) { let indexPath = NSIndexPath(item: item, section: 0) let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath) attributes.zIndex = item var height = standardHeight if indexPath.item == featuredItemIndex { let yOffset = standardHeight * nextItemPercentageOffset y = self.collectionView!.contentOffset.y - yOffset height = featuredHeight } else if item == (featuredItemIndex + 1) && item != numberOfItems { let maxY = y + standardHeight height = standardHeight + max((featuredHeight - standardHeight) * nextItemPercentageOffset, 0) y = maxY - height } frame = CGRect(x: 0, y: y, width: width, height: height) attributes.frame = frame cache.append(attributes) y = frame.maxY } } /* Return all attributes in the cache whose frame intersects with the rect passed to the method */ override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if attributes.frame.intersects(rect) { layoutAttributes.append(attributes) } } return layoutAttributes } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { return self.cache[indexPath.row] } /* Return true so that the layout is continuously invalidated as the user scrolls */ override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { return true } } 
+6
source share
1 answer

Just added that in super.viewDidLoad a view controller that has a UICollection view, and now it works as expected.

This is a quick fix, and I think there is a better way to fix this control by improving the prefetch functions from iOS10.

 if #available(iOS 10.0, *) { collectionView!.prefetchingEnabled = false } 
0
source

All Articles