This sample app demonstrates two custom layout subclasses:
ColumnFlowLayout — A UICollectionViewFlowLayout subclass that arranges cells in a list format for narrow screens, or as a grid for wider screens. See “For a Simple Grid, Size Cells Dynamically,” below.
MosaicLayout — A UICollectionViewLayout subclass that lays out cells in a mosaic-style, nonconforming grid. See “For a Complex Grid, Define Cell Sizes Explicitly,” below.
The app opens to the Friends view controller, which uses a column flow layout to display a list of people. Tapping any cell takes you to the Feed view controller, which uses a mosaic layout to display photos from the user’s photo library.
Tapping the cloud icon to the right of the navigation bar demonstrates batched animations for inserting, deleting, moving, and reloading items in the collection view. For more information, see “Perform Batch Updates,” below. Using pull-to-refresh on the collection view resets the data.
For a Simple Grid, Size Cells Dynamically
ColumnFlowLayout is a subclass of UICollectionViewFlowLayout that uses the size of the collection view to determine the width of its cells. If only one cell will fit comfortably horizontally, the cells are arranged to occupy the entire width of the collection view. Otherwise, multiple columns of cells are displayed with a fixed width.
In practice, on iPhone devices in portrait mode, ColumnFlowLayout displays a single vertical column of cells. In landscape mode, or on an iPad, it displays a grid layout.
Use the prepare() function to compute the available screen width of the device and set the itemSize property accordingly.
MosaicLayout is a UICollectionViewLayout subclass that displays an arbitrary number of cells with differing sizes and aspect ratios. It’s used by FeedViewController to display images from the user’s photo library. Cells are organized into rows in one of four styles, from a single cell to multiple cells in varying layouts.
Calculate Cell Dimensions
The prepare() method is called whenever a layout is invalidated. Override this method to calculate the position and size of every cell, as well as the total dimensions for the entire layout.
Override layoutAttributesForElements(in:), defining the layout attributes for a geometric region. The collection view calls this function periodically to display items, which is known as querying by geometric region.
Also provide the layout attributes for a specific item by implementing layoutAttributesForItem(at:). The collection view calls this function periodically to display one particular item, which is known as querying by index path.
Because these functions are called often, they can affect the performance of your app. To make them as efficient as possible, follow the example code as closely as you can.
Handle Bounds Changes
The shouldInvalidateLayout(forBoundsChange:) function is called for every bounds change from the collection view, or whenever its size or origin changes. This function is also called frequently during scrolling. The default implementation returns false, or, if the size and origin change, it returns true.
For optimum performance, this sample performs a binary search inside layoutAttributesForElements(in:) instead of a linear search of the attributes it needs for each element in a given bounds area.
Perform Batch Updates
Tapping the top-right button in the navigation bar triggers the collection view to perform a batch update of multiple animated operations (insert, delete, move, and reload) of its collection view cells all at the same time.
Within a call to performBatchUpdates(_:completion:), all insert, delete, move, and reload operations are animated simultaneously. In this sample, batch updates are made by processing an array of PersonUpdate objects, each of which encapsulates one update:
insert with a Person object and insertion index.
delete with an index.
move from one index to another.
reload with an index.
First, the reload operations are performed without animation because no cell movement is involved: