UICollectionView Tutorial: Getting Started

Get hands-on experience with UICollectionView by creating your own grid-based photo browsing app using the Flickr API. By Owen L Brown.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Subclassing Your UICollectionViewController

While UICollectionViewController does a lot, you generally need to make a subclass so you can add additional behavior beyond what UIKit provides for free.

Go to File ▸ New ▸ File. Choose iOS ▸ Source ▸ Cocoa Touch Class template and click Next. Name the new class FlickrPhotosViewController, making it a subclass of UICollectionViewController.

The template has a lot of code. The best way to understand what it does is to start from scratch.

Open FlickrPhotosViewController.swift and replace the code in the file with:

import UIKit

final class FlickrPhotosViewController: UICollectionViewController {
  // MARK: - Properties
  private let reuseIdentifier = "FlickrCell"
}

Next, add a constant for section insets, which you’ll use later, right below reuseIdentifier:

private let sectionInsets = UIEdgeInsets(
  top: 50.0,
  left: 20.0,
  bottom: 50.0,
  right: 20.0)

You’ll fill in the rest of the gaps as you progress through the tutorial.

Go back to Main.storyboard. Then, select the collection view controller. In the Identity inspector, set the Class to FlickrPhotosViewController to match your new class:

Set the UICollectionViewController subclass

Now it’s time to fetch Flickr photos to show in the collection view.

Fetching Flickr Photos

Your first task for this section is to say the section title ten times fast.

OK, just kidding.

Flickr is an image-sharing service with a publicly accessible, and dead-simple, API for developers. With the API, you can search for photos, add photos, comment on photos and much more.

You need an API key to use the Flickr API. If you’re working on a real app, sign up for one at Flickr’s website.

However, for test projects like this, Flickr has a sample key that rotates out every so often. You can use the key without signing up.

Simply perform any search at Flickr’s website and copy the API key from the URL at the bottom. It starts after the &api_key=" goes to the next &. Paste it in a text editor for later use.

For example, if the URL is:

http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=6593783efea8e7f6dfc6b70bc03d2afb&;format=rest&api_sig=f24f4e98063a9b8ecc8b522b238d5e2f

then the API key is: 6593783efea8e7f6dfc6b70bc03d2afb

Note: If you use the sample API key, remember it changes nearly every day. If you do this tutorial over days, you might have to get a new API key often. For this reason, it might be easier to get your API key from Flickr if you plan to spend several days on this project.

With that settled, it’s time to explore the Flickr API classes.

Flickr API Classes

Since this tutorial is about UICollectionView and not the Flickr API, the project includes classes that abstract the Flickr search code.

The Flickr support consists of two classes and a struct:

  • FlickrSearchResults: A struct which wraps up a search term and the results found for that search.
  • FlickrPhoto: Data about a photo retrieved from Flickr: its thumbnail, image and metadata information such as its ID. There are also some methods for building Flickr URLs and some size calculations. FlickrSearchResults contains an array of these objects.
  • Flickr: Provides a simple block-based API to perform a search and return a FlickrSearchResult.

Feel free to take a look at the code. It’s pretty simple and might inspire you to use Flickr in your projects!

Before you can search Flickr, you need to enter an API key. Open Flickr.swift. Replace the value of apiKey with the API key you obtained earlier.

It’ll look something like this:

let apiKey = "hh7ef5ce0a54b6f5b8fbc36865eb5b32"

Now, it’s time to do a little prep work before hooking into Flickr.

Preparing Data Structures

In this project, each search you perform displays a new section in the collection view with the results, rather than replacing the previous section. In other words, if you search for ninjas and then pirates, you’ll see a section of ninjas and a section of pirates in the collection view. Talk about a recipe for disaster!

To create these separate sections, you need a data structure to keep each section’s data separate. An array of FlickrSearchResults will do the trick.

Open FlickrPhotosViewController.swift. Add the following properties below sectionInsets:

private var searches: [FlickrSearchResults] = []
private let flickr = Flickr()

searches is an array that keeps track of all the searches made in the app. flickr is a reference to the object that searches for you.

Next, add the following extension to the bottom of the file:

// MARK: - Private
private extension FlickrPhotosViewController {
  func photo(for indexPath: IndexPath) -> FlickrPhoto {
    return searches[indexPath.section].searchResults[indexPath.row]
  }
}

photo(for:) is a convenience method that gets a specific photo related to an index path in your collection view. You’ll access a photo for a specific index path a lot, and you don’t want to repeat code.

You’re now ready to get your Flickr search on!

Getting Good Results

When the user taps Search after typing in a query, you want the search to execute. You already connected the text field’s delegate outlet to your collection view controller. Now you can do something about it.

Open FlickrPhotosViewController.swift. Add an extension to hold the text field delegate methods:

// MARK: - Text Field Delegate
extension FlickrPhotosViewController: UITextFieldDelegate {
  func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    guard 
      let text = textField.text, 
      !text.isEmpty 
    else { return true }

    // 1
    let activityIndicator = UIActivityIndicatorView(style: .gray)
    textField.addSubview(activityIndicator)
    activityIndicator.frame = textField.bounds
    activityIndicator.startAnimating()

    flickr.searchFlickr(for: text) { searchResults in
      DispatchQueue.main.async {
        activityIndicator.removeFromSuperview()

        switch searchResults {
        case .failure(let error) :
          // 2
          print("Error Searching: \(error)")
        case .success(let results):
          // 3
          print("""
            Found \(results.searchResults.count) \
            matching \(results.searchTerm)
            """)
          self.searches.insert(results, at: 0)
          // 4
          self.collectionView?.reloadData()
        }
      }
    }

    textField.text = nil
    textField.resignFirstResponder()
    return true
  }
}

Here’s a code breakdown:

  1. After adding an activity view, you use the Flickr wrapper class to search Flickr asynchronously for photos that match the given search term. When the search completes, you call the completion block with the result set of FlickrPhoto objects and any errors.
  2. You log any errors to the console. Obviously, in a production app, you would want to show the user these errors.
  3. Then, you log the results and add them at the beginning of the searches array.
  4. Finally, you refresh the UI to show the new data. You use reloadData(), which works as it does in a table view.

Build and run. Perform a search in the text box. You’ll see a log message in the console indicating the number of search results like this:

Found 20 matching bananas

Note that the Flickr helper limits the results to 20 to keep load times down.

Unfortunately, you don’t see any photos in your collection view! A collection view doesn’t do much unless you implement the relevant data source and delegate methods, similar to a table view.

You’ll do that in the next section.