Understanding the UICollectionViewFlowLayout and its delegate

The layout that UICollectionView provides out of the box is a grid layout. All items are evenly spaced and sized. To visualize this, open up the storyboard file, select the cell, give it a background color, and run the app. This will make the grid layout really obvious, and it also shows how the constraints nicely center the UI elements inside of the cell.

The ease of use and the performance of the UICollectionView make this a great way for your app to use a grid layout. However, the current implementation of the grid is not perfect yet. The grid looks alright on an iPhone 6s; but on an iPhone SE, the layout looks like it's falling apart and it doesn't look much better when viewed on an iPhone 6s plus. Let's see if we can fix this by changing some settings.

In the storyboard, select the Collection View Flow Layout in the Document Outline. In the Attributes Inspector, you can change the scroll direction for a UICollectionView. This is something that a UITableView couldn't do; it only scrolls vertically. If you don't have enough contacts to make the collection view scroll, refer back to Chapter 1, UITableView Touch Up . The workaround presented there can easily be adapted for the collection view.

When you switch to the Size Inspector, you'll see that there's options available that changes the cell's spacing and size. You might know that an iPhone SE is 320-points wide, so let's update the item size for the layout object from 110 to 106. This will make a grid that has cells with just a single pixel of spacing on the iPhone SE. Also, update the minimum spacing properties for cells and lines to a value of 1. These values indicate the minimum spacing that should be taken into account for the cells in the layout. In practice, the spacing could be more if this allows the cells to fit better. However, it will never be less than the value specified.

If you build and run now, your layout will look great on the iPhone SE. However, larger phones have different spacing between the cells, whereas the line spacing is always just one point. Luckily, we can dynamically manipulate the layout in a code.

You just learned that UICollectionViewFlowLayout provides a pretty powerful solution for grid layouts, but it's not perfect. Different screen sizes require different cell sizes, and we simply can't set this through an interface builder. You probably want your layouts to be as dynamic and flexible as possible so they always look great.

UICollectionViewFlowLayout has a delegate protocol called UICollectionViewDelegateFlowLayout, which allows you to implement a few customization points for the layout. For instance, you can dynamically calculate cell sizes or manipulate the cell spacing. In this case, we'll leave the minimum cell spacing as it is, we want a space between cells that is as small as possible, but not smaller than one pixel.

The line spacing should be the same as the cell spacing, and the cell size should be dynamic so it covers as much of the horizontal space as possible. The method that should be implemented for custom cell sizes is collectionView(_:layout:sizeForItemAt:). We'll use this to return a value that's approximately a third or less of the width of the UICollectionView and 90 points in height. Add the following code to ViewController.swift. To conform to the UICollectionViewDelegateFlowLayout protocol use the following code:

class ViewController: UIViewController, UICollectionViewDataSource, 
  UICollectionViewDelegateFlowLayout 

Also add the following method:

func collectionView(_ collectionView: UICollectionView, layout 
  collectionViewLayout: UICollectionViewLayout, sizeForItemAt 
  indexPath: IndexPath) -> CGSize { 
    return CGSize(width: floor((collectionView.bounds.width - 2) / 3), height: 90) 
} 

This method is called for every cell, and it dynamically calculates cell sizes at the time they are needed. All that this method does is figure out how wide a cell can be, based on the width of the collection view it belongs to. From the collection view width, 2 is subtracted because that's the minimal amount of spacing between items so that space can't be used in this calculation.

If you run the app now, you'll notice that the spacing between cells is always nice and tight. However, the spacing between lines is slightly off. You may have to look real close to notice it; but it will be off by a little bit when compared to the spacing between cells. This can be fixed by implementing the collectionView(_:layout:minimumLineSpacingForSectionAt:) method. This method is called to figure out the spacing between rows. By using a minimum width of 1, combined with a calculation similar to how the cell width was determined, you can figure out what the correct line spacing should be as follows:

func collectionView(_ collectionView: UICollectionView, layout 
  collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt 
  section: Int) -> CGFloat { 
    let cellsPerRow: CGFloat = 3 
    let widthRemainder = (collectionView.bounds.width - 
      (cellsPerRow-1)).truncatingRemainder(dividingBy: cellsPerRow) 
      / (cellsPerRow-1) 
    return 1 + widthRemainder 
} 

First, a variable with the amount of cells per row is set. This is three for the current layout. Then, the same calculation is done as before when calculating cell size, this time the result will be the remainder of this calculation. The remainder is then divided by the amount of gutters that will be on screen. The calculation works like this because the remainder is the amount of pixels that will be distributed between the gutters. In order to get the spacing between each gutter, you need to divide by the amount of gutters. Finally, the return value is 1 + widthRemainder. 1 is the minimum spacing that we always want, and widthRemainder is the extra spacing that will be added to these gutters. If you check the spacing now, they will be exactly equal between both the cells and the lines.

The combination of what's been provided out of the box and what's been gained by implementing just a couple of delegate methods is extremely powerful. We can create beautiful, tight grids with just a few calculations. However, sometimes you would want more than just a grid. Maybe, you're looking for something that looks a bit more playful? A layout where cells are layed out as if they have been scattered across the screen evenly? This is all possible by implementing a custom UICollectionViewLayout.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.20.231