Expanding Cells in iOS Collection Views

Learn how to make expanding cells in iOS collection views, as in the Ultravisual app. By Naeem Shaikh.

Leave a rating/review
Download materials
Save for later
Update note: Naeem Shaikh updated this tutorial for Swift 4.2, iOS 12, and Xcode 10. Chris Lowe wrote the original article.

Expanding collection view cells


When Apple introduced collection views with iOS 6 in 2012, many people thought they were the answer to the roadblocks and limitations of customizing table views. Some even proposed to deprecate UITableView.

Out of the box, UICollectionView is a powerful and ultra-flexible way to present data in your apps. However, one downside is that, without some level of customization, your app looks bland and fails to stand out among the millions of apps in the App Store.

In this tutorial, you’re going to take a rather plain “templated” app of different colored items and turn it into a visually appeasing way to browse RWDevCon inspiration talk sessions using a collection view.

You’ll add awesome parallax effects and a featured cell item, as well as dabble in subtle fading and transitions to make it really stand out from other apps. The end result is a look and feel similar to the UltraVisual app. If you haven’t seen it, get it now — it’s free!

Note: This tutorial requires a basic knowledge of UICollectionViewController, or its close cousin UITableViewController, and an understanding of the basics of the Swift language. If you’re not familiar with it, you can learn more about it in our written or video tutorial series:

Ready to stand out with your collection view? Read on!

Getting Started

Download the Ultravisual Kit project using the Download Materials button at the top or bottom of this tutorial. Open the Starter project in Xcode 10. Inside, you’ll find a simple storyboard project with several classes. These are conveniently separated into folders to help you navigate around the project.

The folders contain the following:

  • Assets: Contains Images.xcassets, Inspirations.plist and Inspirations.xcassets, which the app uses to load the images for the RWDevCon speakers.
  • Controllers: Contains InspirationsViewController.swift, a UICollectionViewController subclass.
  • Extensions: Contains UIColor+Palette.swift and UIImage+Decompression.swift, which provide convenience methods that handle color conversion and image decompression, respectively. The starter project uses the color palette and, in a future step, you’ll switch to using images and the decompression method.
  • Layouts: Contains UltravisualLayout.swift, which is the meat of the project. As a subclass of UICollectionViewLayout, UICollectionView asks this class for a definition of how to properly lay out and place the items in the collection view. Inside, you’ll find a set of constants and convenience methods that you’ll use to simplify setting up your layout. Additionally, it provides a simple custom cache of UICollectionViewLayoutAttributes that are used to modify each cell.
  • Models: Contains the data models for the background images (Inspiration.swift) and session details (Session.swift); they are separated for the ultimate MVC-pattern win!
  • Views: Contains InspirationCell.swift, which handles setting the properties of the collection view cell.

Build and run the starter project, and you’ll see the following:

Starter Kit Screenshot

The app looks decent (if you want a random color palette app, that is), but it doesn’t do much right now. That’s where your job comes in: You’ll turn this clunker into a beautiful app that’s designed to quickly scan through the list of inspirational speakers and topics from RWDevCon.

The technical magic of this project comes from managing the different cell item states:

  • The standard cell (what you see in the starter project) that transitions into the “next up” cell, which grows a bit larger.
  • The featured cell, which is the largest of all.

You’ll also use a little trickery — modifying the z-index of the cell as it scrolls — for a sweet stacking effect.

Creating Multiple Cell Sizes

First, you’ll create the featured cell. It’ll be roughly twice as large as the standard cell, so a user can clearly see which item is selected.

Jump to UltravisualLayout.swift and go to prepare(). After this line of code:

cache.removeAll(keepingCapacity: false)

Add a few new local variables that you’ll use across each item:

let standardHeight = UltravisualLayoutConstants.Cell.standardHeight
let featuredHeight = UltravisualLayoutConstants.Cell.featuredHeight
var frame = CGRect.zero
var y: CGFloat = 0    

Next, add the following code to loop through each item in the collection view and adjust the item’s state accordingly:

for item in 0..<numberOfItems {
  // 1
  let indexPath = IndexPath(item: item, section: 0)
  let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath)
  // 2
  attributes.zIndex = item
  var height = standardHeight
  // 3
  if indexPath.item == featuredItemIndex {
    // 4
    let yOffset = standardHeight * nextItemPercentageOffset
    y = collectionView!.contentOffset.y - yOffset
    height = featuredHeight
  } else if indexPath.item == (featuredItemIndex + 1)
    && indexPath.item != numberOfItems {
    // 5
    let maxY = y + standardHeight
    height = standardHeight + max(
      (featuredHeight - standardHeight) * nextItemPercentageOffset, 0
    y = maxY - height
  // 6
  frame = CGRect(x: 0, y: y, width: width, height: height)
  attributes.frame = frame
  y = frame.maxY

There is a lot going on in the loop, so here's a breakdown:

  1. Create an index path to the current cell, then create default attributes for it.
  2. Prepare the cell to move up or down. Since the majority of cells will not be featured — there are many more standard cells than the single featured cells — it defaults to the standardHeight.
  3. Determine the current cell's status — featured, next or standard. In the case of the latter, you do nothing.
  4. If the cell is currently in the featured-cell position, calculate the yOffset and use that to derive the new y value for the cell. After that, you set the cell's height to be the featured height.
  5. If the cell is next in line, you start by calculating the largest y could be (in this case, larger than the featured cell) and combine that with a calculated height to end up with the correct value of y, which is 280.0 — the height of the featured cell.
  6. Lastly, set some common elements for each cell, including creating the frame, setting the calculated attributes, and updating the cache values. The very last step is to update y so that it's at the bottom of the last calculated cell so that you can move down the list of cells efficiently.

For the collection view to know which layout to use, you'll need to tell UICollectionView to use UltravisualLayout as its layout class.

First, open Main.storyboard and select the Collection View inside the Document Outline.

Collection View Selection

Next, open the Attributes inspector and set the Layout dropdown to Custom, and then set the Class to UltravisualLayout.

Layout Selection

Finally, before building, go to InspirationsViewController.swift and, in viewDidLoad(), remove the last two lines:

let layout = collectionViewLayout as! UICollectionViewFlowLayout
layout.itemSize = CGSize(width: collectionView!.bounds.width, height: 100)

These two lines were the basic layout to show the equally sized multi-colored cells. Now that you've specified layout code in UltravisualLayout and set the custom layout class in the storyboard, you no longer need these lines.

Build and run, and you'll see this:


Notice that the top cell is much larger, effectively showcasing a featured cell. As you scroll, the cell below the featured cell expands and overlaps the current featured cell. What a nice effect!