The “traditional” way of creating a swipeable gallery of images uses a UIScrollView and a series of views containing images which are placed in a line inside the scrollView. It’s a technique that works - but it’s fiddly to set up, and it doesn’t scale well. With more than a very few images in the gallery, you need to start implementing lazy loading to prevent the gallery gobbling up memory.
If you think of a gallery as being a UITableView laid on its side, then it’s pretty obvious that we could use something similar and take advantage of the lazy loading of cells that a table view offers. Prior to iOS6, sideways-scrolling tableViews were tricky to set up - but iOS6 came to the rescue with UICollectionView.

This is a worked example of setting up a full-screen paged image gallery using UICollectionView that lazily-loads images, and supports dynamic resizing of images during device rotation.
Setting up the project
Create a Single View Application project in Xcode. You should end up with an AppDelegate and a single view controller and xib file.
Wiring up the collection view
In the main view controller’s .h file, add the UICollectionViewDelegate and UICollectionViewDatasource protocols:
@interface CMFViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate>
Then switch to the .m file and create two new properties:
@interface CMFViewController ()
@property (nonatomic, weak) IBOutlet UICollectionView *collectionView;
@property (nonatomic, strong) NSArray *dataArray;
@end
In the main view controller’s ‘.xib’, drag and drop a UICollectionView object into the view, and then connect it to the collectionView property. You’ll also need to connect the UICollectionView object’s dataSource and delegate outlets to the File's Owner object.

Creating the data
For the purposes of this demo, I’ve assumed that there’s an Assets directory in the project’s folder structure, and that contains a series of images.

The first step is to load the names of the files in this directory into the dataArray property - I’ve done this with a seperate method:
-(void)loadImages {
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Assets"];
self.dataArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:sourcePath error:NULL];
}
This method gets called in the view controller’s viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
[self loadImages];
}
Feeding the collection view with data
There are three methods that need to be implemented to feed the collection view with data:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView;
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
Taking these in turn, the first is to tell the collection view how many sections it has. This being a simple example, there’s only one:
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
Next, the collection view needs to know how many items it’s going to be displaying in total - which is the number of images that live in the Assets directory:
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [self.dataArray count];
}
The last third method is where the real work takes place, and the collection view cells are created. Before we can do this, though, we need to go ahead and create the cells.
Creating the UICollectionViewCells
There’s a couple of ways of creating UICollectionViewCells - the nib-based route, or creating a custom subclass. The latter is the more flexible, so this is the route we’ll take here.
Create a new UICollectionViewCell subclass called CMFGalleryCell. This is going to need one public property declared in the .h file:
@property (nonatomic, strong) NSString *imageName;
and one public method:
-(void)updateCell;
Next we’re going to need a .xib file for the cell, so create a new that and give it a name - mine’s CMFGalleryCell.xib.
By default, the new view comes into life with a UIView inside it - we don’t need this, so delete it and replace it with a UICollectionViewCell object. The canned object is 50 points x 50 points, which isn’t what we need - change the dimensions to 320 x 548 (for a 4-inch device) or 320 x 440 (for a 3.5” device).
While you’re there, change the color of the background to white, and drop a UILabel to the center of the view. This label won’t show anything useful, but will help to show the effect of paging through the collection view when it runs for the first time.

Next, we need to tell this UICollectionViewCell that it’s actually an instance of our custom class. Select the object, and in the Identity inspector change the custom class field to CMFGalleryCell.

Once the object knows what it’s supposed to be, we need to update the CMFGalleryCell class so that it can extract the UICollectionViewCell from the nib file when the need arises. There’s several ways of doing this - here’s the way I tend to do it, in the initWithFrame: method:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:@"CMFGalleryCell" owner:self options:nil];
if ([arrayOfViews count] < 1) {
return nil;
}
if (![[arrayOfViews objectAtIndex:0] isKindOfClass:[UICollectionViewCell class]]) {
return nil;
}
self = [arrayOfViews objectAtIndex:0];
}
return self;
}
Stepping through this, the first task is to call the superclass’s initWithFrame: method. Assuming that this succeeds, we can then grab the contents of the xib file into the arrayOfViews array. This should have the UICollectionViewCell object as the first item in the array - but if that isn’t the case, the two if statements will allow things to fail gracefully.
Assuming things are where they’re supposed to be, the penultimate line grabs the UICollectionViewCell from the arrayOfViews, and the last line returns it.
Feeding the collection view with cells
Now we’re ready to send the UICollectionView some cells to display. Switch back to the view controller, and import the custom cell class:
#import "CMFGallleryCell.h"
Now we’re in a position to register this class for use. This needs to be done before the collectionView is displayed, so I tend to stick this in a configureCollectionView method which is called from viewDidLoad::
- (void)viewDidLoad {
[super viewDidLoad];
[self loadImages];
[self setupCollectionView];
}
The setupCollectionView method is going to expand as we go along, but for the moment it needs to register the cell subclass for use:
-(void)setupCollectionView {
[self.collectionView registerClass:[CMFGalleryCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
}
That’s pretty self-explanatory - it tells the collectionView that there’s a class called CMFGalleryCell that will be associated with the reuse identifier cellIdentifier
Now we can start to implement the method that does the heavy lifting of cell creation:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CMFGalleryCell *cell = (CMFGalleryCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
return cell;
}
A lot takes place behind the scenes here. Every time the collection view needs a cell, it will call the collectionView:cellForItemAtIndexPath: method on its dataSource, and expect a UICollectionViewCell object to be returned.
The method first attempts to dequeue an existing cell to save the memory overhead of creating a new one. If for any reason there isn’t a reusable cell available in the queue (for example, if it’s the first cell being created) then behind the scenes, the collection view’s dataSource object will create a new instance of one. As far as we’re concerned, we’re guaranteed to get a cell returned - either a shiny new one, or one that’s been used prevously and is ready for recycling.
Configuring the collection view at runtime
We’ve got to the point where there’s a collection view and collection view cells (albeit ones that don’t do anything yet). In order to get the cells displayed correctly, there’s a bit more configuration required.
This means expanding the existing setupCollectionView method:
-(void)setupCollectionView {
[self.collectionView registerClass:[CVGGallleryCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
[self.collectionView setPagingEnabled:YES];
[self.collectionView setCollectionViewLayout:flowLayout];
}
In order to know how to display content, the collection view relies on a `UICollectionViewLayout` object which provides the necessary information to allow the collection view to draw the cells into the user interface.
The linear grid-based layout is sufficiently generic that Apple have provided a `UICollectionViewLayout` subclass called `UICollectionViewFlowLayout`. Out of the box, this will draw lines of cells in a grid arrangement. You don't need to understand too much about the detail at this stage, but these are the important characteristics that we need to implement in order to create the gallery view:
Creating the flow layout
The flow layout is created with
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
Configuring the scroll direction
Flow layouts can scroll in either vertical (up-and-down table view style) or horizontal (side-to-side gallery style) directions. We’re going to use horizontal:
[flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
Setting the cell spacing
To get the seamless gallery effect, we need to setup the collection view to display the cells without any spacing:
[flowLayout setMinimumInteritemSpacing:0.0f];
[flowLayout setMinimumLineSpacing:0.0f];
Setting up paging
Getting each image to “snap” to the screen as it scrolls is a nice touch, and UICollectionView provides that functionality out of the box:
[self.collectionView setPagingEnabled:YES];
Setting the item size
The collection view needs be informed of the size of the item, which can be set statically or dynamically via a delegate protocol. If you’re only ever going to have one size of cell, then you can simple set the item size with:
[flowLayout setItemSize:CGSizeMake(320, 548)];
However, it can also be set dynamically through the UICollectionViewDelegateFlowLayout protocol. To use this method, first switch to the view controller’s header file and add the declaration for the protocol:
@interface CMFViewController : UIViewController <UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>
Then switch back to the implementation file and add the collectionView:layout:sizeForItemAtIndexPath: method:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return CGSizeMake(320, 548);
}
Applying the layout to the collection view
Once the UICollectionViewFlowLayout is setup and configured, it needs to be applied to the collection view itself:
[self.collectionView setCollectionViewLayout:flowLayout];
At this point, if you build and run the project, you’ll see a working collection view with paging cells:

Configuring the cells at runtime
Functional as this is, it doesn’t yet look much like a gallery. To get the images requires a bit more work, firstly in the view controller:
Feeding the cells with data
Thinking back to the data that we’re working with, we’ve got an NSArray of NSStrings containing image filenames. We’re going to pass that filename to the cell, and then load the image into the cell.
Passing the filename to the cell can be done when the cell is dequeued (or created) in the collectionView:cellForItemAtIndexPath: method:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
CMFGalleryCell *cell = (CMFGalleryCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
NSString *imageName = [self.dataArray objectAtIndex:indexPath.row];
[cell setImageName:imageName];
[cell updateCell];
return cell;
}
Loading the image into the cell
We’ll do this in the cell’s updateCell method
-(void)updateCell {
NSString *sourcePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Assets"];
NSString *filename = [NSString stringWithFormat:@"%@/%@", sourcePath, self.imageName];
UIImage *image = [UIImage imageWithContentsOfFile:filename];
[self.imageView setContentMode:UIViewContentModeScaleAspectFit];
[self.imageView setImage:image];
}
One final tweak - switch to the cell’s .xib file, remove the UILabel and change the background colour of the UICollectionViewCell to ‘default’.
Run the app again, and now you’ve got a paging gallery:

Handling device rotation
At the moment, if you rotate the simulator (or device) you’ll see an error in the console:
UICollectionGallery[26606:c07] the behavior of the UICollectionViewFlowLayout is not defined because:
UICollectionGallery[26606:c07] the item height must be less that the height of the UICollectionView minus the section insets top and bottom values.
That’s occuring because when the device rotates, the collection view cell is taller than the collection view itself. To fix this, we need to do two things:
Making the collection view cell size dynamic
We want the collection view cell to be the same size as the collection view, regardless of whether the device is in portrait or landscape orientation. If we’d set the collection view cell size statically (in the setupCollectionView method) that would be tricky - but we future-proofed ourselves by making this a dynamic method.
The collection view cell size is controlled by the collectionView:layout:sizeForItemAtIndexPath: method - so we can update this to return whatever the current size of the UICollectionView is:
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
Forcing the collection view to update
If you run the app again, you’ll see that the error still occurs. That’s because having changed the size of the collection view cells, we need to force the collection view to redraw itself. We can do this while the device is rotating by hooking into the willRotateToInterfaceOrientation:duration: method:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self.collectionView reloadData];
}
But this now creates another glitch - when you rotate into a different orientation, the paging will be slightly off. To fix this, we need to extend the willRotateToInterfaceOrientation:duration: method:
-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
CGPoint currentOffset = self.collectionView.contentOffset;
float newOffsetX;
if ((toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)) {
// currently in portrait
float offsetIndex = (int)currentOffset.x / [[UIScreen mainScreen] bounds].size.width ;
newOffsetX = offsetIndex * [[UIScreen mainScreen] bounds].size.height;
} else {
// currently in landscape
float offsetIndex = (int)currentOffset.x / [[UIScreen mainScreen] bounds].size.height;
newOffsetX = offsetIndex * [[UIScreen mainScreen] bounds].size.width;
}
CGPoint newOffset = CGPointMake(newOffsetX, 0);
[self.collectionView setContentOffset:newOffset];
[self.collectionView reloadData];
}
This checks the current offset, figures out how many images along the Y axis that offset equates to, then calculates a new offset to move the collectionView to the appropriate location.

Going further
Although this example is based on a full-screen gallery, there’s no reason why it can’t be adapted to fit into a pre-existing interface. It’s just a case of amending the sizes of the collection view and cells, and tweaking the paging increments accordingly.
Source code
A complete project with source code is downloadable from https://github.com/timd/UICollectionViewGallery.git