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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
UICollectionView Tutorial: Getting Started
25 mins
- Anatomy of a UICollectionView
- Using the UICollectionViewLayout
- Introducing FlickrSearch
- Starting Your Collection
- Adding Search
- Subclassing Your UICollectionViewController
- Fetching Flickr Photos
- Flickr API Classes
- Preparing Data Structures
- Getting Good Results
- Feeding the UICollectionView
- UICollectionViewDataSource
- UICollectionViewFlowLayoutDelegate
- Creating Custom UICollectionViewCells
- Subclassing UICollectionViewCell
- Where to Go From Here?
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:
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
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:
- 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. - You log any errors to the console. Obviously, in a production app, you would want to show the user these errors.
- Then, you log the results and add them at the beginning of the searches array.
- 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.