iOS Programming Recipe 14: Implementing A UICollectionViewController

UICollectionView is great for displaying information in a grid or other custom layouts. In iOS 6 you’ll see collection views most likely used in many apps such as the podcasts app for viewing different podcasts. Since in most cases you’ll want to use UICollectionViewController, we’ll focus on that for this recipe.

Assumptions

  • You are familiar with Xcode and setting up a single view controller. If not check out our recipe on Xcode first.

Setting Up The View

Go ahead and create a new single view controller project. I titled mine “CollectioViewFun”. When the project opens up, You’ll want to delete the single view controller from the storyboard and drag a new collection view controller in it’s place. This should look like the following:

CollectionViewController

Now since our class type has changed from a standard view controller to a collection view controller we’ll want to change the class type in the viewController.h file as well. Go ahead and change the class name from ViewController to CollectionViewController as well. Don’t forget to change all instances of the term “ViewController” in both the .m and .h files:


@interface CollectionViewController : UICollectionViewController <UICollectionViewDataSource>

Since we are going to be using the DataSource  protocol of this class I also added the protocol deceleration.

Now select the entire view from the storyboard and change the class to the CollectionViewController class:

ChangeToCollectionViewControllerClass

Adding A Cell Class

Since each cell will be basically a seperate view, we’ll want to create a class for the cell itself. Go ahead and create a new class of type UICollectionViewCell by clicking the “+” in the bottom left corner and choosing “New File…” . Then Choose the following values in the next two prompts:

CreateACellClass

ChoosingCellSubclass

Now from the storyboard, Select the cell and change the class from the identity inspector to “Cell” just as you did previously for the CollectionViewController.

ChangeCellClass

Set the Identifier for the cell to “MY_CELL” by selecting the cell from the storyboard and chaing the “Identifier” field under the attributes inspector on the right hand pane.

Select the Collection View Controller window and change the Collection View Cell Size to 150 x 150 points.

ChangeCollectionViewCellSize

Since I’ll be displaying images in the collection view, we’ll want to modify the cell class to have an image view. On the storyboard, Drag an Image View object into the cell and resize to the view to the same size as the Cell.

Now control click and drag from that newly created image view into the Cell.h File and create a new outlet titled imageView:

CreateImageViewOutlet

Alright, Now we’re ready to start modifying the data source methods.

Constructing The Data Source

Choose the CollectionVewController.h file and command click on the protocol decleration “UICollectionViewDataSource” which should bring up the UICollectionView.h file. This file will show you all of the required protocols for this class as well as some optional ones.

For the collectionView there are two required data source protocols and they are “numberOfItemsInSection” and “cellForItemAtIndexPath”

so we’ll go ahead and add those to our CollectinViewController.m class:


#pragma mark - Collection View Data Sources

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{

}

// The cell that is returned must be retrieved from a call to - dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{

}

Since I am a bit of a truck enthusiest and I have a bunch of pictures of trucks: I am going to display a few trucks in this collection view controller. Sorry guys, No Chevys.

So lets start off by creating an array of image names in the ViewDidLoad method of the CollectionViewController. Modify the CollectionViewController.h and .m files as follows:

CollectionViewController.h


@interface CollectionViewController : UICollectionViewController    &lt;UICollectionViewDataSource,UICollectionViewDelegate&gt;

@property (strong, nonatomic) NSArray *truckImages;

@end

CollectionViewController.m viewDidLoad method


- (void)viewDidLoad
{
    [super viewDidLoad];
    self.truckImages = @[@"silverFord.jpg",@"orangeFord.jpg",@"yellowFord.jpg",@"greenFord.jpg"];
}

Also, we’ll want to import our Cell class into the CollectionViewController class so we can use it. Right after #import “CollecitonViewController.h” in the CollectionViewController.m insert on the next line: #import “Cell.h”


#import "CollectionViewController.h"
#import "Cell.h"

Now we’re ready to edit the Data source methods.

The numberOfItemsInSection data source is pretty easy as it just returns the number of elements in our truck array (In this Case 4). Now we could just put “return 4;” in the method and just be done with it, but that’s not good practice. Instead we’ll return the count of the array. Modify the numberOfItemsInSection as follows:


- (NSInteger)collectionView:(UICollectionView *)collectionView  numberOfItemsInSection:(NSInteger)section
{
    return self.truckImages.count;
}

the cellForItemAtIndexPath method is bit more tricky, but not too bad. Modify this method as follows:


- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    Cell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MY_CELL" forIndexPath:indexPath];
    //cell.imageView.image = [UIImage imageNamed:self.truckImages[0]];
    UIImage *truckImage = [[UIImage alloc] init];
    truckImage = [UIImage imageNamed:[self.truckImages objectAtIndex:indexPath.row]];
    cell.imageView.image = truckImage;
    return cell;
}

Here we are creating a new Cell object and setting the imageView image to the corresponding truck images at the cell indexpath. Once we have our Cell setup we return it.

At this point if you run the application you should see a grid view with some pictures:

ViewAfterDataSource

The Last piece of this tutorial is creating a segue to a new View Controller which will give some info about the trucks. First we’ll need to set up the Navigation Controller.

Adding The Navigation Controller

From the storyboard drag a navigation controller from the object library onto the storyboard and delete the tableview controller that is connected to it. Control click from the navigation controller and drag to the collection view controller and choose “Root View Controller” when prompted.

Make sure the incoming arrow which indicates the starting view is on the navigation controller. You can click and drag it over to the the navigation controller if it is not.

Now drag a new view controller onto the storyboard and control click and drag from the cell in document outline to the new view controller. When prompted choose “push” from the modal type drop down.

Next add a new UIImageView object to the new view controller and a label underneath it. Make the Image View 300 x 300 points.

When you are done with these steps your storyboard should look like this:

NavigationControllerStoryboard

Create a new class for the new view and give it a subclass of UIViewController. Title the new class ImageDetailViewController and set the class of the new view controller to ImageDetailViewController from the Identity inspector with the view controller selected on the storyboard.

Now create outlets by control clicking and dragging into the ImageDetailViewController.h from the UIImageView and Label we just created. Title these outlets truckImageView and truckDetailLabel.

Now that the Navigation Controller is set up we can implement the prepareForSegue method.

Preparing For The Segue

Now you may have noticed that we are not using the delegate in this instance. The problem with a delegate in this instance is prepareForSegue will be called before the delegate so anything inside the delegate will get ignored. To get around this we’ll add some extra logic to the prepareForSegue method that determines the collectionView indexPath.

First we’ll need to create another array to hold the descriptions for the truck images. Do so by modifying the CollectionViewController.h and m file as follows:

CollectionViewController.h:


@interface CollectionViewController : UICollectionViewController

@property (strong, nonatomic) NSArray *truckImages;
@property (strong,nonatomic) NSArray *truckDescriptions;

@end

CollectionViewController.m viewDidLoad method:


- (void)viewDidLoad
{
    [super viewDidLoad];
    self.truckImages = @[@"silverFord",@"orangeFord",@"yellowFord",@"greenFord"];
    self.truckDescriptions = @[@"50's Silver Ford", @"50's Orange Ford", @"70's Yellow Ford",@"50's Green Ford" ];
}

To start with, add the following method to the end of the collectionViewController.m file:


#pragma mark - Prepare for Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

}

Before editing the prepareForSegue method we’ll need to import the ImageDetailViewController class into this one as we did fo the Cell class.


#import "CollectionViewController.h"
#import "Cell.h"
#import "ImageDetailViewController.h"

Now we can add code the prepareForSegue method. Go ahead and modify it as follows:


#pragma mark - Prepare for Segue
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    UICollectionViewCell *cell = (UICollectionViewCell *)sender;
    NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];

    ImageDetailViewController *imageDetailViewController = (ImageDetailViewController *)segue.destinationViewController;
    imageDetailViewController.truckImage = [UIImage imageNamed:[self.truckImages objectAtIndex:indexPath.row]];
    imageDetailViewController.truckLabelText = [self.truckDescriptions objectAtIndex:indexPath.row];
}

This code does a few things. First we create an instance of the UICollectionViewCell class to get the sender. Using that, we create an NSIndexPath object. The indexPath will be what we’ll use to know which cell was selected.

Then we create an instance of our ImageDetailViewController class and set the image and label text values using the indexPath to find values in the truckImages array and truckDescriptions array.

Now this code won’t work yet until we set some properties in the ImageDetailViewController class. Edit the ImageDetailViewController.h file as follows:


@interface ImageDetailViewController : UIViewController

@property (strong, nonatomic) IBOutlet UIImageView *truckImageView;
@property (strong, nonatomic) IBOutlet UILabel *truckDetailLabel;
@property (strong, nonatomic) UIImage *truckImage;
@property (strong, nonatomic) NSString *truckLabelText;

@end

Then update the ImageDetailViewController.m viewDidLoad method as follows:


- (void)viewDidLoad
{
    [super viewDidLoad];
    self.truckImageView.image = self.truckImage;
    self.truckDetailLabel.text = self.truckLabelText;
}

This Step is needed because we can’t directly write to the UIImage and UILabel properties.

Now Everything should work! Run it and press one of the collection view icons and you should see a screen that looks like this.

FinalCollectionViewImage

 

I know this tutorial was a bit long winded, but I wanted to show the use of the navigation controller in this recipe as well. Setting up the navigation portion of a UICollectionViewController is close to the same process we would use to add a navigation controller to a UITableViewController.

Another Thing to note  is by choosing the UICollectionViewController object in the beginning automatically connected our datasource and delegate connections for us. If you have implemented all of this code and nothing is showing up, chances are you need to make these connections.

That’s it! Hope you enjoyed this!

About Joe Hoffman

I am An Electrical Engineer that spends a lot of my off time doing web development and other programming. Currently I am trying to expand my knowledge with iOS and I find that writing tutorials helps me learn more thoroughly as well as provide some useful info to others.

Comments

  1. Hi, Joe – Thanks a lot for putting this tutorial together. I tried working through a few others that failed miserably before coming across yours.

    I just have one question. When I ran the app, I got the warning that a segue initiated directly from a view controller must have an identifier. When I add an identifier, the warning goes away, but my segue doesn’t fire. Any ideas?

    Best,
    Andrew

    • Joe Hoffman says:

      Hi Andrew,

      Tried to email you so you can send the source, but I guess you left a bogus email 🙁 .

      • Hey, Joe – Oops! It was early and caffeine levels were low. I’ve since figured out what I was doing wrong. Thanks again for the great tutorial.

        • Joe Hoffman says:

          Good to hear!

          • I’ve got a new question for you, Joe. I searched high and low and couldn’t find a way to remove the padding automatically generated between cells. Do you know how that can be done? In other words, I want the image borders to be touching, thereby eliminating the blank space in between.

          • Joe Hoffman says:

            Hi Andrew,

            Sorry for the delay. I think the best route is by subclassing a UITableView cell and creating a new custom table view cell.

  2. This does not work. I’m using Xcode 5 and the directions do not work. It stops making sense soon after “Adding a Cell Class” The following line:
    Now from the storyboard, Select the cell and change the class from the identity inspector to “Cell” just as you did previously for the CollectionViewController.
    Why? There is still only one scene in the story board. Making the objective C class did not create a new scene. If there is still only supposed to be one scene, then why did we change it from it’s original name to CollectionViewController and now to Cell? Then it goes on to change the identity for Cell to MY_CELL, but there is nothing under the attributes inspector to do that? Any help would be appreciated it.

  3. my comment was deleted?
    i was asking about the segue because i cant get the transition to work to have a detail view controller…
    is there a chance someone can help me with the full code?
    thanks very very much for your help

  4. Great tutorial, but have one problem. /CollectioViewFun/CollectionViewController.m:86:33: Property ‘jewelryImage’ not found on object of type ‘ImageDetailedViewController’. I made sure that I have @property (strong, nonatomic) UIImage *jewelryImage; declared and ImageDetailedViewController.h imported to CollectionViewController.m
    What is wrong?

    • Joe Hoffman says:

      Hey, did you add the property to the ImageDetailedViewController or the collectionViewController? It looks like you may have put the property in the wrong spot.

      • I Have added it to the ImageDetailViewController.h like tutorial says, but still no luck. And I imported #import “ImageDetailViewController.h” to the CollectionViewController.m
        But this
        ImageDetailViewController.truckImage = [UIImage imageNamed:[self.truckImages objectAtIndex: indexPath.row]];

        still doesn’t work. I really appreciate your help. thanks.

  5. My bad, find my errors, sorry! Misspelling, i did ImageDetailViewController.truckImage instead imageDetailViewController.truckImage ))

  6. hello,

    Your tutorial is good. I have done the coding till navigation controller. I am not able to find the image in the cell? Everything runs fine but not the image,

    • Joe Hoffman says:

      Hey Pradeep,

      First insure that all the image names are correct, and the extensions are correct as well. Then make sure the array of image names is not empty. put a break point in on the cell creation and type “po self.” in the output window.

      Then make sure the images have been added to the project, you’ll need to drag them into the project if you haven’t.

  7. Matthew Whyte says:

    Hi Joe

    I’ve made a tableView and I am trying to make a collectionView but the app crashes as soon as it pushes to the detalViewController. Please can you send me the source code of this project so that I can have a better understanding.

    Thank you in advance.

    Best regards
    Matthew Whyte

Speak Your Mind

*

css.php
Privacy Policy