I had a project recently where I needed to create a horizontally scrolling paged grid of grids. The app started as a single 3 x 3 grid so I did the obvious and created a UIView with sub views. This evolved into each subview being a UIButton subclass that took a number of parameters and rendered accordingly. Over the course of the next 9 months or so this became increasingly completely and unwieldy. Also, the nine-cell grid evolved into a paged scrolling process and especially on an iPad, with large images, using rounded corners and several pages crashing due to memory usage became a very real problem. It’s interesting that adding round corners to a view causes the views to be cached as an image which takes a LOT of memory. That is why having a rounded view using Quartz corner radius in a UITableView causes sluggish, juddery bahaviour.

The solution I dedided was to create something similar to a UITableView where only the cells in view are held in memory and the outgoing cells are put on a stack for recycling. This led to the creation of MSGridView (see https://github.com/martingsaunders/MSGridView). MSGridView uses a central view to hold a number of cells and manages a recycling queue of cells. The separation into view and cells allows numerous grid cell types to be used meaning that a very flexible and manageable code base results. Also, in terms of memory usage the app I was working on went from hundreds of megabytes to tens of bytes.

To have a play with MSGridView head over to https://github.com/martingsaunders/MSGridView and run the sample project. If you wish to install it just drop the MSGridView.h, MSGridView.m, MSGridViewCell.h and MSGridViewCell.m files into your project.

Usage

In your view controller (e.g. in viewDidLoad) initialise and add the view

MSGridView *gridView = [[MSGridView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
[self.view addSubview:gridView];

Then you need to setup delegate and datasource

gridView.gridViewDelegate = self;
gridView.gridViewDataSource = self;

From there you can set a few parameters:

Paging – by default this is on. Disable with:

self.pagingEnabled = NO;

Inner spacing (gaps between grids):

[gridView setInnerSpacing:CGSizeMake(100, 100)];

Delegate / DataSource methods

See MSGridView.h but…

Required Datasource methods:

// returns the cell for a specific grid row/column, row/column set
-(MSGridViewCell *)cellForIndexPath:(NSIndexPath*)indexPath inGridWithIndexPath:(NSIndexPath *)gridIndexPath;

// Returns the number of supergrid rows
-(NSUInteger)numberOfGridRows;

// Returns the number of supergrid rows
-(NSUInteger)numberOfGridColumns;

All basically the same as a UITableView

Optional methods:

// for each given super grid, how many rows do we have? Default 1
-(NSUInteger)numberOfRowsForGridAtIndexPath:(NSIndexPath *)indexPath;

// for each given super grid, how many columns do we have? Default 1
-(NSUInteger)numberOfColumnsForGridAtIndexPath:(NSIndexPath *)indexPath;

// the size of an individual grid cell row / column. Default is the view width / number rows or columns
-(float)heightForCellRowAtIndex:(NSUInteger)row forGridAtIndexPath:(NSIndexPath *)gridIndexPath;
-(float)widthForCellColumnAtIndex:(NSUInteger)column forGridAtIndexPath:(NSIndexPath *)gridIndexPath;

// detect a touch event on a cell
-(void)didSelectCellWithIndexPath:(NSIndexPath*) indexPath;

Custom MSGridViewCells

Just subclass MSGridViewCell and modify in the same way as a custom UIViewCell. At current this is just an empty UIView so there are no default labels etc. Make sure you init with:

- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)identifier.