The layoutAttributesForElements(_:) method is a more complex method to implement. This method is used by the collection view when it needs the layout for multiple items at once. The collection view will specify the visible section of its contents, and the layout is expected to figure out which layout attributes to return for the items that fall within the specified CGRect.
Even though only a certain number of cells is displayed at the same time, the collection view knows about the fact that there's a lot more content outside the viewport that may not be visible. If the collection view needs to suddenly jump to a certain point in the collection view that wasn't visible before, it will use the layout object to ask for the layout attributes for items that will become visible after the jump. Based on these attributes, the collection fetches the required cells and renders them on screen. This is possible because UICollectionViewLayoutAttributes doesn't just contain the frames for the cells, it also knows about the IndexPath for all cells.
This is all pretty complex and just reading about it won't make it any less complex. The most important takeaway from this is that the collection view layout should be able to figure out which cells, or layout attributes, overlap with any given CGRect inside of the collection view. It's probably best that we dive right into an example so you can immediately see what we need to achieve.
To determine which attributes have overlap with a given CGRect, the intersects method on CGRect is used as follows:
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
return layoutAttributes.filter { attributes in
attributes.frame.intersects(rect)
}
}
For something that required a couple of paragraphs to explain, the preceding code is pretty simple. To find out which attributes overlap the given CGRect, the filter method is used on the layoutAttributes that were calculated by prepare. Filter is a powerful and convenient method, just like the map method that was demonstrated previously, to convert fetched CNContact objects to HCContact objects.
The filter method loops over every item in the layoutAttributes array and calls the supplied closure (which must return true or false), once for each attribute instance in the array. If the closure returns true, the attributes instance will be included in the output array; if it returns false, then it will be omitted. Once again, the extensive preparation pays off because we were able to come up with a very simple, concise, and fast implementation for this calculation.