Custom UICollectionViewLayout Tutorial With Parallax
Introduced in iOS6, UICollectionView is a first-class choice for advanced customization and animation. Learn more in this UICollectionViewLayout tutorial. By .
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
Custom UICollectionViewLayout Tutorial With Parallax
30 mins
- Getting Started
- Layout Settings
- Layout Attributes
- The Role of UICollectionViewLayout
- Step 1: Subclassing the UICollectionViewLayout Class
- Step 2: Implementing the CollectionViewLayout Core Process
- Step 3: Adopting the CustomLayout
- Adding Stretchy, Sticky and Parallax Effects
- Affine Transforms
- Transforming Visible Attributes
- Where to Go From Here?
Note: This tutorial uses Xcode 9.0 and Swift 4.
Introduced in iOS 6 and refined with new features in iOS 10, UICollectionView
is the first-class choice to customize and animate the presentation of data collections in iOS applications.
A key entity associated with UICollectionView
is the UICollectionViewLayout
. The UICollectionViewLayout
object is responsible for defining the attributes of all the elements of a collection view such as cells, supplementary views and decoration views.
UIKit
offers a default implementation of UICollectionViewLayout
called UICollectionViewFlowLayout
. This class lets you set up a grid layout with some elementary customizations.
This UICollectionViewLayout
tutorial will teach you how to subclass and customize the UICollectionViewLayout
class. It will also show you how to add custom supplementary views, stretchy, sticky and parallax effects to a collection view.
UICollectionViewLayout
tutorial requires an intermediate knowledge of Swift 4.0, an advanced knowledge of UICollectionView
, affine transforms and a clear understanding of how the core layout process works in the UICollectionViewLayout
class.If you’re unfamiliar with any of these topics, you could read the Apple official documentation…
…or, you can check out some of the excellent tutorials on the site!
- UICollectionView Tutorial: Getting Started
- UICollectionView Tutorial: Reusable Views and Cell Selection
- UICollectionView Custom Layout Tutorial: Pinterest
- Video Tutorial: Collection Views
At the end of this UICollectionViewLayout
tutorial you’ll be able to implement a UICollectionView
like the following:
Are you ready to win the Jungle Cup? Let’s go!
Getting Started
Download the starter project for this tutorial and open it in Xcode. Build and run the project.
You’ll see some cute owls laid out in a standard UICollectionView
with sections headers and footers like the following:
The app presents the Owls Team’s players who are taking part in the Jungle Soccer Cup 2017. Section headers show their roles in the team while footers display their collective strength.
Let’s have a closer look at the starter project:
Inside JungleCupCollectionViewController.swift file you’ll find the implementation of a UICollectionViewController
subclass conforming to the UICollectionDataSource
protocol. It implements all the required methods plus the optional method for adding supplementary views.
The JungleCupCollectionViewController
adopts MenuViewDelegate
too. It’s a protocol to let the collection view switch its data source.
In the Reusable Views folder, there are subclasses of UICollectionViewCell
for the cells, and UICollectionReusableView
for section header and section footer views. They link to their respective views designed in the Main.storyboard file.
Besides that, there are the custom supplementary views the CustomLayout
requires. Both the HeaderView
and MenuView
classes are subclasses of UICollectionReusableView
. They’re both linked to their own .xib files.
MockDataManager.swift file holds the data structures for all the teams. For convenience’s sake, the Xcode project embeds all the necessary assets.
Layout Settings
The Custom Layout folder deserves special attention because it contains two important files:
- CustomLayoutSettings.swift
- CustomLayoutAttributes.swift
CustomLayoutSettings.swift implements a structure with all the layout settings. The first group of settings deals with collection view’s elements sizes. The second group defines the layout behaviors, and the third sets up the layout spacings.
Layout Attributes
The CustomLayoutAttributes.swift file implements a UICollectionViewLayoutAttributes
subclass named CustomLayoutAttributes
. This class stores all the information the collection view needs to configure an element before displaying it.
It inherits the default attributes such as frame
, transform
, transform3D
, alpha
and zIndex
from the superclass.
It also adds some new custom properties:
var parallax: CGAffineTransform = .identity
var initialOrigin: CGPoint = .zero
var headerOverlayAlpha = CGFloat(0)
parallax
, initialOrigin
and headerOverlayAlpha
are custom properties you’ll use later in the implementation of stretchy and sticky effects.
If you implement custom layout attributes, you must also override the inherited isEqual
method to compare the values of your properties. Starting with iOS 7, the collection view does not apply layout attributes if those attributes have not changed.
UICollectionViewLayoutAttributes
, you must conform to NSCopying
by implementing an appropriate method for copying your custom attributes to new instances.
If you implement custom layout attributes, you must also override the inherited isEqual
method to compare the values of your properties. Starting with iOS 7, the collection view does not apply layout attributes if those attributes have not changed.
Currently the collection view can’t display all the teams yet. For the moment, supporters of Tigers, Parrots and Giraffes have to wait.
No worries. They will be back soon! CustomLayout
will solve the problem :]
The Role of UICollectionViewLayout
The main goal of a UICollectionViewLayout
object is to provide information about the position and visual state of every element in a UICollectionView
. Please keep in mind a UICollectionViewLayout
object isn’t responsible for creating the cells or supplementary views. Its job is to provide them with the right attributes.
Creating a custom UICollectionViewLayout
is a three-step process:
- Subclass the abstract class
UICollectionViewLayout
and declare all the properties you’ll need to perform the layout calculations. - Perform all needed calculations to provide every collection view’s element with the right attributes. This part will be the most complex because you’re going to implement the
CollectionViewLayout
core process from scratch. - Make the collection view adopt the new
CustomLayout
class.
Step 1: Subclassing the UICollectionViewLayout Class
Inside the Custom Layout group you can find a Swift file named CustomLayout.swift which contains a CustomLayout
class stub. Within this class you’ll implement the UICollectionViewLayout
subclass and all the Core Layout processes.
First, declare all the properties CustomLayout
needs to calculate the attributes.
import UIKit
final class CustomLayout: UICollectionViewLayout {
// 1
enum Element: String {
case header
case menu
case sectionHeader
case sectionFooter
case cell
var id: String {
return self.rawValue
}
var kind: String {
return "Kind\(self.rawValue.capitalized)"
}
}
// 2
override public class var layoutAttributesClass: AnyClass {
return CustomLayoutAttributes.self
}
// 3
override public var collectionViewContentSize: CGSize {
return CGSize(width: collectionViewWidth, height: contentHeight)
}
// 4
var settings = CustomLayoutSettings()
private var oldBounds = CGRect.zero
private var contentHeight = CGFloat()
private var cache = [Element: [IndexPath: CustomLayoutAttributes]]()
private var visibleLayoutAttributes = [CustomLayoutAttributes]()
private var zIndex = 0
// 5
private var collectionViewHeight: CGFloat {
return collectionView!.frame.height
}
private var collectionViewWidth: CGFloat {
return collectionView!.frame.width
}
private var cellHeight: CGFloat {
guard let itemSize = settings.itemSize else {
return collectionViewHeight
}
return itemSize.height
}
private var cellWidth: CGFloat {
guard let itemSize = settings.itemSize else {
return collectionViewWidth
}
return itemSize.width
}
private var headerSize: CGSize {
guard let headerSize = settings.headerSize else {
return .zero
}
return headerSize
}
private var menuSize: CGSize {
guard let menuSize = settings.menuSize else {
return .zero
}
return menuSize
}
private var sectionsHeaderSize: CGSize {
guard let sectionsHeaderSize = settings.sectionsHeaderSize else {
return .zero
}
return sectionsHeaderSize
}
private var sectionsFooterSize: CGSize {
guard let sectionsFooterSize = settings.sectionsFooterSize else {
return .zero
}
return sectionsFooterSize
}
private var contentOffset: CGPoint {
return collectionView!.contentOffset
}
}
That’s a fair chunk of code, but it’s fairly straightforward once you break it down:
- An
enum
is a good choice for defining all the elements of theCustomLayout
. This prevents you from using strings. Remember the golden rule? No strings = no typos. - The
layoutAttributesClass
computed property provides the class to use for the attributes instances. You must return classes of typeCustomLayoutAttributes
: the custom class found in the starter project. - A subclass of
UICollectionViewLayout
must override thecollectionViewContentSize
computed property. - The
CustomLayout
needs all these properties in order to prepare the attributes. They’re allfileprivate
except thesettings
, sincesettings
could be set up by an external object. - Computed properties used as syntactic sugar to avoid verbose repetitions later.
Now that you’re done with declarations, you can focus on the Core Layout process implementation.