Collection Views in OS X Tutorial

In this collection views in OS X tutorial, you’ll discover the joy of arranging things in beautiful, flowing interfaces for the desktop. By Gabriel Miro.

Leave a rating/review
Save for later

A collection view is a powerful mechanism for laying out an ordered set of data items visually. It sounds boring when explained in technical terms, so think of apps that do this: Finder and Photos; these apps let you tab through files in a gallery (visual) layout.

When released with OS X 10.5, NSCollectionView offered a handy means of arranging a group of objects in a grid of identically sized items displayed in a scrollable view.

OS X 10.11, aka El Capitan, gave NSCollectionView a major overhaul inspired by UICollectionView from iOS.

Among other things, the new NSCollectionView API adds support for:

  • Sections with optional headers/footers
  • Variable-size items
  • Customizable layouts
  • Cell reuse

In this collection views in OS X tutorial, you’ll discover the joy of arranging things in beautiful, flowing interfaces for the desktop. You’ll close the gap on UX between mobile and desktop and enjoy greater control over your apps by building SlidesMagic — your own grid-based image browsing app.

This tutorial assumes that you know the basics of writing OS X apps. If you’re new to OS X you should take a look at the OS X tutorials available here, and then come back to learn about collection views.

The show is about to start, so have a seat and get ready for your own personal magic show by NSCollectionView.

Behind the Scenes of Collection Views

The NSCollectionView is the main view — the stage, if you will — where the magic show happens. It displays visual items and contains these key components:


NSCollectionViewLayout – New to this version, this lets you specify a layout by setting the collection view’s collectionViewLayout. It’s an abstract class from which all concrete layout classes inherit.

NSCollectionViewFlowLayout – Provides a flexible grid-like layout, and you can achieve your desired results for most apps with it.

NSCollectionViewGridLayout – Matches NSCollectionView‘s simple grid layout behavior pre OS X 10.11 but doesn’t support sections and all the goodies that come with the new API.


Sections and NSIndexPath – Allows for grouping of items into sections. The items form an ordered list of sections, each containing an ordered list of items. Each item is associated with an index that comprises of a pair of integers (section, item) encapsulated in an NSIndexPath instance. When grouping of items into sections isn’t required, you still have at least one section (by default).


The Collection View Items

Like many other Cocoa frameworks, items in the collection view follow the MVC design pattern.

The Model and the View – The items’ content comes from your model’s data objects. Each individual object that becomes visible gets its own view in the larger collection view. The structure of these individual views are defined in a separate nib (file extension “.xib”).

The Controller – The nib mentioned above is owned by an instance of NSCollectionViewItem, which is is a descendant of NSViewController, or a subclass. It mediates the flow of information between the items’ views and model objects. Generally, you subclass NSCollectionViewItem. When items are not of the same kind, you define a different subclass and nib for each variant.

Supplementary Views

To display extra information in the collection view that’s not part of an individual item, you’d use supplementary views; some common implementations of these are section headers and footers.

The Collection View Data Source and Delegates

  • NSCollectionViewDataSource – Introduced with the new API in OS X 10.11, the populates the collection view with items and supplementary views.
  • NSCollectionViewDelegate – Handles events related to drag-and-drop, selection and highlighting.
  • NSCollectionViewDelegateFlowLayout – Lets you customize a flow layout.
Note: You can populate a table view by using a data source or Cocoa Bindings. This collection views in OS X tutorial covers the data source.

Introducing SlidesMagic

SlidesMagic the app you’re going to build is a simple image browser. It’s pretty cool, but don’t get all excited and delete Photos from your Mac just yet.

It retrieves all image files from a folder on the file system and displays them and their names in an elegant collection view. It’s actually kind of magical when you think about it! The finished app will look like this:


Download the starter project here and open it in Xcode. Build and run:


At this point, it appears to be an empty window, but it has hidden features that will become the foundation of an image browser.

When SlidesMagic launches, it automatically loads all the images from the system’s Desktop Pictures folder. From Xcode‘s console log, you can see the file names.

desktop photos

That list in the console is an indicator that the model-loading logic is in place. You can choose another folder by selecting the File \ Open Another Folder… menu.

Overview of the Starter Project code

The starter project provides functionality that is not directly related to collection views but specific to SlidesMagic.



  • ImageFile.swift: Encapsulates a single image file
  • ImageDirectoryLoader.swift: Helper class that loads image files from the disk


The application has two main controllers:

  • WindowController.swiftwindowDidLoad() is responsible for the initial sizing of the window on the left half of the screen. The openAnotherFolder method is invoked by File \ Open Another Folder… menu item, and it presents a standard open dialog to choose a different folder.
  • ViewController.swiftviewDidLoad() opens the Desktop Pictures folder as the initial folder to browse, and loadDataForNewFolderWithUrl() is used by openAnotherFolder from WindowController.

Creating the Collection View

Open Main.storyboard. Go to the Object Library, and drag a Collection View into the view of the View Controller Scene.


Note: You may have noticed Interface Builder added three views instead of just one. That’s because the Collection View is embedded inside a Scroll View (which also has another subview called Clip View). When you’re instructed to select the Collection View, make sure you’re not selecting the Scroll View or the Clip View.

If you build now you’ll see an error:

Main.storyboard: Unknown segue relationship: Prototype 

Along with the collection view, Xcode also added an NSCollectionViewItem, connected with a Prototype segue.

Therein you have the cause of your build error — it’s this type of segue that’s causing the trouble. This is a vestige (or bug if you like…) of the OS X 10.10 and earlier API model that prevents you from using collection view items in storyboards.

The fix is to delete it and reincarnate it in a separate xib file.

Select the Collection View Item and press Delete to, well, delete it.


Now the build error is gone.

Resize the Bordered Scroll View so it takes up the whole area of the parent view. Then, select Editor \ Resolve Auto Layout Issues \ Add Missing Constraints to add the Auto Layout constraints.


You need to add an outlet in ViewController to access the collection view. Open ViewController.swift and add the following inside the ViewController class definition:

@IBOutlet weak var collectionView: NSCollectionView!

Open Main.storyboard, and select the View Controller inside the View Controller Scene.

Open the Connections Inspector and find the collectionView element within the Outlets section. Connect it to the collection view by dragging from the button next to it to the collection view control in the canvas.


Configure the Collection View Layout

You’ve got options: You can set the initial layout and some of its attributes in Interface Builder, or you can set them programmatically.

For SlidesMagic, you’ll take the programmatic approach.

Open ViewController.swift and add the following method to ViewController:

private func configureCollectionView() {
  // 1
  let flowLayout = NSCollectionViewFlowLayout()
  flowLayout.itemSize = NSSize(width: 160.0, height: 140.0)
  flowLayout.sectionInset = NSEdgeInsets(top: 10.0, left: 20.0, bottom: 10.0, right: 20.0)
  flowLayout.minimumInteritemSpacing = 20.0
  flowLayout.minimumLineSpacing = 20.0
  collectionView.collectionViewLayout = flowLayout
  // 2
  view.wantsLayer = true
  // 3
  collectionView.layer?.backgroundColor = NSColor.blackColor().CGColor

Here’s what you’re doing in this method:

  1. Creating an NSCollectionViewFlowLayout and setting its attributes and the collectionViewLayout property of the NSCollectionView.
  2. For optimal performance, NSCollectionView is designed to be layer-backed. So, you’re setting an ancestor’s wantsLayer property to true.
  3. Making an addition related to the layer that’s specific to SlidesMagic, setting the collection view’s background color to black.

You need to call this method when the view is created, so add this to the end of viewDidLoad():


Build and run:


At this point, you have a black background and a layout. You’ve set the stage for your magic show!

Loading Items in the Collection View

To load items, you need to call its reloadData() method, which causes the collection view to discard and redisplay any currently visible items.

You’d typically call this method when the model changes.

Open ViewController.swift and add this code at the end of loadDataForNewFolderWithUrl(_:):


This makes it so that selecting File \ Open Another Folder… calls this method. it loads a new model then calls reloadData().

Creating a Collection View Item

Just because you removed NSCollectionViewItem from the storyboard doesn’t mean you don’t need it. :] Here’s how to bring it back the right way.

Go to File \ New \ File…, select OS X \ Source \ Cocoa Class and click Next.

Set the Class field to CollectionViewItem, the Subclass of field to NSCollectionViewItem, and check Also create XIB for user interface.


Click Next, and in the save dialog, select Controllers from Group and click Create.

Open CollectionViewItem.swift and replace the whole class with this:

class CollectionViewItem: NSCollectionViewItem {

  // 1
  var imageFile: ImageFile? {
    didSet {
      guard viewLoaded else { return }
      if let imageFile = imageFile {
        imageView?.image = imageFile.thumbnail
        textField?.stringValue = imageFile.fileName
      } else {
        imageView?.image = nil
        textField?.stringValue = ""
  // 2
  override func viewDidLoad() {
    view.wantsLayer = true
    view.layer?.backgroundColor = NSColor.lightGrayColor().CGColor

In here, you:

  1. Define the imageFile property that holds the model object to be presented in this item. When set, its didSet property observer sets the content of the item’s image and label.
  2. Change the background color to the item’s view.

Add Controls to the View

The View in the nib is the root view for a subtree of controls to be displayed in the item. You’re going to add an image view and a label for the file name.

Open CollectionViewItem.xib.

Add an NSImageView:

  1. From the Object Library, add an Image View to View.
  2. Select and click Pin from the Auto Layout toolbar to set its constraints.
  3. Set the top, leading and trailing constraints to 0, the bottom to 30 and click Add 4 Constraints.


  4. To fix the Auto Layout issues, select Editor \ Resolve Auto Layout Issues \ Update Frames.


Add a label:

  1. From the Object Library, add a Label below the Image View.
  2. LabelInPlace

  3. Click the Pin button. Set the top, bottom, trailing and leading constraints to 0, and then click Add 4 Constraints.
  4. Screen Shot 2015-12-09 at 20.11.30

  5. Select Editor \ Resolve Auto Layout Issues \ Update Frames to update its position.

Select the Label, and in the Attributes Inspector set the following attributes:

  1. Alignment to center
  2. Text Color to white
  3. Line Break to Truncate Tail


Now you need to connect the controls to the imageView and the textField outlets:

  1. Select File’s Owner and show the Connections Inspector.
  2. Next, drag from the button next imageView to the Image View control to connect them.
  3. In the same way, connect the textField outlet to Label.


Add a Top Level CollectionViewItem to the Nib

The File’s Owner in the nib — of type CollectionViewItem — is just a placeholder. You still need to instantiate it.

CollectionView's method makeItemWithIdentifier(_:forIndexPath:) instantiates the collection view item, and it requires the nib to contain a single top-level instance of either NSCollectionViewItem or a subclass thereof.

You need to make it appear.

Drag a Collection View Item from the Object Library and drop it into Document Outline. Select it, and in the Identity Inspector, set its Class to CollectionViewItem.


Populate the Collection View

You need to implement the data source methods so the view knows the answers to these questions:

  1. How many sections are in the collection?
  2. How many items are in each section?
  3. Which item is associated with a specified index path?

Meet your data source method: NSCollectionViewDataSource protocol.

Put it into action now — open ViewController.swift and add the following extension at the end of the file:

extension ViewController : NSCollectionViewDataSource {
  // 1
  func numberOfSectionsInCollectionView(collectionView: NSCollectionView) -> Int {
    return imageDirectoryLoader.numberOfSections
  // 2
  func collectionView(collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
    return imageDirectoryLoader.numberOfItemsInSection(section)
  // 3
  func collectionView(collectionView: NSCollectionView, itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath) -> NSCollectionViewItem {
    // 4
    let item = collectionView.makeItemWithIdentifier("CollectionViewItem", forIndexPath: indexPath)
    guard let collectionViewItem = item as? CollectionViewItem else {return item}
    // 5
    let imageFile = imageDirectoryLoader.imageFileForIndexPath(indexPath)
    collectionViewItem.imageFile = imageFile
    return item
  1. This method provides the number of sections. When your app doesn’t support sections, you can omit this method because a single section will be assumed. When SlidesMagic launches, the model imageDirectoryLoader is set to return the value 1.
  2. This is one of two required methods for NSCollectionViewDataSource. Here you return the number of items in the section specified by the section parameter. Upon launch, SlidesMagic has a single section, so you set the model to return the total number of images in the folder.
  3. This is the second required method. It returns a collection view item for a given indexPath.
  4. The collection view’s method makeItemWithIdentifier(_:forIndexPath:) instantiates an item from a nib where its name equals the value of the identifier parameter. In this case, it’s "CollectionViewItem". First, it attempts to reuse an unused item of the requested type, and if nothing is available it creates a new one.
  5. This code gets the model object for the given NSIndexPath and sets the content of the image and the label.
Note: The ability of the collection view to recycle unused collection view items provides a scalable solution for large collections. Items associated with model objects that aren’t visible are the objects that get recycled.

Set the Data Source

A collection view without data is like a magic act with no slight of hand — pointless and uninspiring. So, your next step is to define the data source; in this case it’s the view controller.

Open Main.storyboard and select the collection view.

Open the Connections Inspector and locate dataSource in the Outlets section. Drag from the adjacent button to the View Controller in Document Outline View.


Build and run.

Voila! It was worth all that work! Your collection view displays images from the Desktop Pictures folder!


If you don’t see images, then something went wrong. You worked through several steps, so you probably just missed something small.

  1. Are all the connections in the Connections Inspector set as instructed?
  2. Did you set the dataSource outlet?
  3. Did you use the right type for custom classes in the Identity Inspector.
  4. Did you add the top level NSCollectionViewItem object and change its type to CollectionViewItem?
  5. Is the value for the identifier parameter in makeItemWithIdentifier identical to the nib name?

Going Multi-Section

MagicSlides is doing some serious magic now. But you’re going to improve it by adding sections.

First, you need to add a check box at the bottom of the view so you can toggle between single and multi-section.

Open Main.storyboard, and in the Document Outline view, select the scroll view’s bottom constraint. Open the Size Inspector and change its Constant to 30.

This is how you move the collection view up to make room for the check box.


Now, drag a Check Box Button from the Object Library into the space below the collection view. Select it, and in the Attributes Inspector, set its Title to Show Sections, and its State to Off.


Then, set its Auto Layout constraints by selecting the Editor \ Resolve Auto Layout Issues \ Add Missing Constraints menu.

Build and run. It should look like this at the bottom:


Now for a little UI. When you click the box, the application needs to change the collection view’s appearance.

Open ViewController.swift and add the following method at the end of the ViewController class:

// 1
@IBAction func showHideSections(sender: AnyObject) {
  // 2
  let show = (sender as! NSButton).state
  imageDirectoryLoader.singleSectionMode = (show == NSOffState)
  // 3

Section by section, here’s what you’re doing:

  1. Calling this method by toggling the state of the checkbox.
  2. Accordingly, retrieving the state of the checkbox and setting data model mode singleSelectionMode, and then calling the model’s setupDataForUrls(_:) method to rearrange the sections structure. The nil value passed means you skip image loading — same images, different layout.

If you’re curious how images are distributed across sections, look up sectionLengthArray in ImageDirectoryLoader. The number of elements in this array sets the max number of sections, and the element values set the number of items in each section.

Now, open Main.storyboard. In the Document Outline, Control-Drag from the Show Sections control over the View Controller. In the black pop-up window click showHideSections: to connect it. You can check if the connection was set properly in the Connections Inspector.


Build and run.


Check Show Sections and watch the layout change.

Because of the “black tail” in the second row, you see section 0 has seven images, but section 1 has five and section 2 has 10 (look at sectionLengthArray). It’s not clear because the sections are rather cozy.

To fix this, open ViewController.swift and modify the layout’s sectionInset property in the configureCollectionView() function.


flowLayout.sectionInset = NSEdgeInsets(top: 10.0, left: 20.0, bottom: 10.0, right: 20.0)

With this:

flowLayout.sectionInset = NSEdgeInsets(top: 30.0, left: 20.0, bottom: 30.0, right: 20.0)

Here you set the bottom and top section insets to 30 to provide better visual separation between sections.

Build and run.


Check Show Sections, and note the additional spacing between sections.

Add Section Headers

The app is looking great so far, but more organization will make it even better.

Another way to see section boundaries is adding a header or footer view. To do this, you need a custom NSView class and will need to implement a data source method to provide the header views to the table view.

To create the header view, select File \ New \ File…. Select OS X \ User Interface \ View and click Next.

Enter HeaderView.xib as the file name and for Group select Resources.

Click Create.


Open HeaderView.xib and select the Custom View. Open the Size Inspector and change Width to 500 and Height to 40.

Drag a label from the Object Library to the left-hand side of Custom View. Open the Attributes Inspector and change Title to Section Number and Font Size to 16.

Drag a second label to the right-hand side of Custom View and change Title to Images Count and Text Alignment to Right.

Now, select the Editor \ Resolve Auto Layout Issues \ Add Missing Constraints menu to add its Auto Layout constraints.

The header view should look like this:


With the interface ready for show time, the next task is to create a custom view subclass for the header view.

Select File \ New \ File… to create a new file.

Choose OS X \ Source \ Cocoa Class and name the class HeaderView, and then make it a subclass of NSView. Click next, and for Group select Views. Click Create.

Open HeaderView.swift and replace the contents of the class with the following:

// 1
@IBOutlet weak var sectionTitle: NSTextField!
@IBOutlet weak var imageCount: NSTextField!

// 2
override func drawRect(dirtyRect: NSRect) {
  NSColor(calibratedWhite: 0.8 , alpha: 0.8).set()
  NSRectFillUsingOperation(dirtyRect, NSCompositingOperation.CompositeSourceOver)

In here, you’re:

  1. Setting up outlets you’ll use to connect to the labels in the nib.
  2. Drawing a gray background.

With the framework in place, the next task is changing the nib file to use this new class and connecting the outlets to the labels.

Open HeaderView.xib and select the Custom View. Open the Identity Inspector. Change the Class to HeaderView.

Screen Shot 2015-12-09 at 22.50.57

In the Document Outline view, Control-Click on the Header View. In the black pop-up window, drag from imageCount to the Images Count label on the canvas to connect the outlet.

Repeat the operation for the second label, dragging from sectionTitle to the Section Number label in the canvas.


Implement the Data Source and Delegate Methods

Your header view is in place and ready to go, and you need to pass the header views to the collection view to implement the collectionView(_:viewForSupplementaryElementOfKind:atIndexPath:) method.

Open ViewController.swift and add the following method to the NSCollectionViewDataSource extension:

func collectionView(collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> NSView {
  // 1
  let view = collectionView.makeSupplementaryViewOfKind(NSCollectionElementKindSectionHeader, withIdentifier: "HeaderView", forIndexPath: indexPath) as! HeaderView
  // 2
  view.sectionTitle.stringValue = "Section \(indexPath.section)"
  let numberOfItemsInSection = imageDirectoryLoader.numberOfItemsInSection(indexPath.section)
  view.imageCount.stringValue = "\(numberOfItemsInSection) image files"
  return view

The collection view calls this method when it needs the data source to provide a header for a section. The method:

  1. Calls makeSupplementaryViewOfKind(_:withIdentifier:forIndexPath:) to instantiate a HeaderView object using the nib with a name equal to withIdentifier.
  2. Sets the values for the labels.

At the end of ViewController.swift, add this NSCollectionViewDelegateFlowLayout extension.

extension ViewController : NSCollectionViewDelegateFlowLayout {
  func collectionView(collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> NSSize {
    return imageDirectoryLoader.singleSectionMode ? NSZeroSize : NSSize(width: 1000, height: 40)

The above method, although technically optional, is a must when you use headers because the flow layout delegate needs to provide the size of the header for every section.

When not implemented, the header won’t show because zero size is assumed. Additionally, it ignores the specified width, effectively setting it to the collection view’s width.

In this case, the method returns a size of zero when the collection view is in single section mode, and it returns 40 when in multiple sections mode.

For the collection view to use NSCollectionViewDelegateFlowLayout, you must connect ViewController to the delegate outlet of NSCollectionView.

Open Main.storyboard and select the collection view. Open the Connections Inspector, and locate the delegate in the Outlets section. Drag from the button next to it to the view controller in the Document Outline.


Build and run.


Check Show Sections and watch your header neatly define sections.

Selection in Collection Views

Collection views support both single and multiple selections. To show an item as selected, you must highlight it.

Before you can do that, you need to make the collection view selectable. Open the Main.storyboard. Then, select the Collection View and in the Attributes Inspector, check Selectable.


Checking Selectable enables single selection, meaning you can click an item to select it. And when you choose a different item, it deselects the previous item and selects item you just picked.

To show an item as selected, you’ll set a white border with borderWith set to 5.0. Non-selected items will get no special treatment.

Open CollectionViewItem.swift. Add the following at the end of viewDidLoad():

// 1
view.layer?.borderWidth = 0.0
// 2
view.layer?.borderColor = NSColor.whiteColor().CGColor
  1. Setting borderWidth to 0.0 initializes the item to show not selected
  2. Sets white for the color when selected

Add the following method at the end of CollectionViewItem class:

func setHighlight(selected: Bool) {
  view.layer?.borderWidth = selected ? 5.0 : 0.0

This method is called to add or remove highlighting.

When the user selects an item in the collection view, you find out via delegate methods. You’ll need to implement those methods, so open ViewController.swift and add this extension at the end of the file:

extension ViewController : NSCollectionViewDelegate {
  // 1
  func collectionView(collectionView: NSCollectionView, didSelectItemsAtIndexPaths indexPaths: Set<NSIndexPath>) {
    // 2
    guard let indexPath = indexPaths.first else {
    // 3
    guard let item = collectionView.itemAtIndexPath(indexPath) else {
    (item as! CollectionViewItem).setHighlight(true)

  // 4
  func collectionView(collectionView: NSCollectionView, didDeselectItemsAtIndexPaths indexPaths: Set<NSIndexPath>) {
    guard let indexPath = indexPaths.first else {
    guard let item = collectionView.itemAtIndexPath(indexPath) else {
    (item as! CollectionViewItem).setHighlight(false)

This implements the necessary NSCollectionViewDelegate methods. Further detail:

  1. When you select an item, NSCollectionView calls this method.
  2. Here you get the selected item — since multiple selection is disabled, it is always the first.
  3. This retrieves an item by its index and highlights it.
  4. The same as the previous method, but it’s called when an item is deselected.

What Happens During Selection Stays in Selection

When selected, an item is set in:

  1. The selectionIndexPaths property of NSCollectionView.
  2. The selected property of NSCollectionViewItem.

It’s critical to understand that when an NSCollectionViewItem instance is recycled, the data set must be refreshed with the data from the new object. Your selected property of NSCollectionViewItem is where you ensure this happens.

Accordingly, in the NSCollectionViewDataSource extension you need to add the following inside collectionView(_:itemForRepresentedObjectAtIndexPath:) method, just before the return statement:

if let selectedIndexPath = collectionView.selectionIndexPaths.first where selectedIndexPath == indexPath {
} else {

This guarantees that selection and highlighting are in sync.

Build and run.


Click an item and you’ll see highlighting. Choose a different image and you’ll see fully functional highlighting. Poof! Magic!

Where to go From Here

Download the final version of SlidesMagic here.

In this collection views in OS X tutorial you went all the way from creating your first ever collection view, through discovering the intricacies of the data source API with sections, to handling selection with the delegate protocol. However, although you covered a lot of ground, you’ve barely explored the capabilities of collection views. For instance, you didn’t look into any of these:

  • “Data Source-less” collection views using Cocoa Bindings
  • Different kind of items
  • Adding and removing items
  • Custom layouts
  • Drag and drop
  • Animations
  • Tweaking NSCollectionViewFlowLayout (sticky headers, for example)

We will be covering some of these topics in upcoming OS X tutorials here on, so be sure to check back over the coming months.

Unfortunately, the documentation for collection views from Apple and other sources is limited, but here are my suggestions:

I wish you a pleasant journey with Collection View in your apps. I look forward to hearing your ideas, experiences and any questions you have in the forums below!

Gabriel Miro


Gabriel Miro


Over 300 content creators. Join our team.