Creating a Bookshelf for your iOS Application

Stephen Zaharuk / Friday, November 9, 2012

As this is my first post, before I begin, let me introduce myself. My name is Steve Zaharuk (SteveZ) and I've been working for Infragistics since 2004. I've worked on a lot of different platforms since I first started here including ASP.Net, Silverlight, WPF and now iOS, where I'm the Product Architect. 

You can follow me on twitter at: @codebystevez

In this blog I plan on showing your the true power of our NucliOS product which was just released last month.  Also, if you're new to iOS and you're coming from a C# background I'll also be discussing my own transition and provide a bunch of tips, tricks, and tutorials on making the leap. 

But now that, thats out of the way, lets get down to business!

If you're anything like me, you probably just want a link to the source code so you can dive right in. So here you go, i've also included a link a the bottom of the post. 

For this tutorial, I'll be using the IGGridVIew control which you can grab as free trial, or if you are as excited as I am about this product you can just purchase it! :) 

So if you haven't done so yet, please run the installer. 

-------Don't worry i'll wait ;) ---------

Great! Now that you've installed the product, and have the full power of NucliOS at your finger tips, lets jump in!

Step 1. Add a reference to to the IGGridView.  To do that, open the Framework dialog in xCode and scroll all the way to the bottom. You should see the IG.Framework file. Select it and add it to your project. 

Note you'll also need to add a reference to the QuartzCore.framework


Now add a new ViewController to your project. 

Then modify your ViewController's header file with the following: 

#import <UIKit/UIKit.h>
#import <IG/IG.h>
@interface ViewController : UIViewController
{
   IGGridView* _gridView;    
   IGGridViewSingleFieldMultiColumnDataSourceHelper* _ds;
}
@end

First we’ll add an import for the IG.Framework. Then we’ll create a field for the IGGridView and a DataSourceHelper, which will help us display our data.

Now lets setup our data. For this sample i’ll be using a collection of thumbnail images that I already had. So I’ll add them to my project and setup a new class to reference them.

@interface Data : NSObject
   @property(nonatomic, readonly)UIImage* thumbnail;  
   @property(nonatomic, retain)NSString* imgName;
@end
@implementation Data
   -(UIImage *)thumbnail
   {
       return [UIImage imageNamed:[self imgName]];
   }
@end

In the viewDidLoad of our ViewController, we’ll first setup our data, put the data in our datasource and create our grid.

- (void)viewDidLoad
{
   [super viewDidLoad]; 

   NSMutableArray* data = [[NSMutableArray alloc]init];

   for(int i = 1; i <= 26="" i="" br="">   {
      Data* photoInfo = [[Data alloc]init];
      photoInfo.imgName = [NSString stringWithFormat:@"thumb%d.jpeg", i];
      [data addObject:photoInfo];
   }

   IGGridViewImageColumnDefinition* col = [[IGGridViewImageColumnDefinition alloc]initWithKey:@"thumbnail" forPropertyType:IGGridViewImageColumnDefinitionPropertyTypeImage];

   col.loadAsync = YES;

   _ds = [[IGGridViewSingleFieldMultiColumnDataSourceHelper alloc]initWithField:col];
   _ds.numberOfColumns = 4;
   _ds.data = data;

   _gridView = [[IGGridView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
   _gridView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;

   [self.view addSubview:_gridView];

   _gridView.headerHeight = 0;
   _gridView.selectionType = IGGridViewSelectionTypeNone;

   _gridView.dataSource = _ds;

}

So what are we doing in that snippet? The first part is just setting up the Data object we created earlier. This is what we’ll use to display our data.

The next part is creating a column definition that points to our “thumbnail” property on our data object. We then create our DataSourceHelper, point it to use the column definition that we just created, and tell it that it should display 4 columns on a shelf.

We then create our gridView to be the full size of the ViewController, and point it to the datasource we just setup.

If you were to run your project right now, you’ll see the following:

Not very pretty eh? :)

But, we’re almost there. We’ve got our data being displayed, in the proper layout. We just need to add some styling to our grid.

For that, we’ll need a few images. Actually a total of 5.

  1. Background Image for the grid
  2. Background Image for the 1st row
  3. Background Image for other rows
  4. Shelf Image for the 1st row
  5. Shelf Image for other rows

So to do this, we need to add a few lines to our viewDidLoad:

_gridView.delegate = self;
_gridView.rowHeight = 100;
_gridView.rowSeparatorHeight = 50;
_gridView.cellContentInset = UIEdgeInsetsMake(30, 10, 0, 15);
UIImageView* bg = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"Bookcase_Universal_TOP"]];
bg.contentMode = UIViewContentModeScaleToFill; _gridView.backgroundView = bg;

The first line sets the delegate of the IGGridView, so that we can provide our shelve images.

The next 3 lines setup the row. These are the 3 properties you’ll need to play with in order to get your images to display properly on the shelving. The rowHeight just determines how big your shelve area is. The rowSeparatorHeight determines how big your shelf is. And the cellContentInset is the really important one. It pushes the content of the cell to make it appear as if it’s sitting on your shelf.

The next couple of lines set the background of the IGGridView. This is important, b/c if you scroll the grid past is bounds, you don’t want to see a white background.

B/c we already setup our delegate, we just have to add 2 more methods to our ViewController and we are done!

-(UIView *)gridView:(IGGridView *)gridView viewForRowSeparatorAtPath:(IGRowPath *)path
{
   NSString * imgPath = (path.rowIndex == 0)? @"Bookcase_Universal_TopShelf.png" : @"Bookcase_Universal.png";
   UIImageView* iv = [[UIImageView alloc]initWithImage:[UIImage imageNamed:imgPath]];
   iv.contentMode = UIViewContentModeScaleToFill;
   return iv;
}
-(UIView *)gridView:(IGGridView *)gridView viewForRowBackgroundAtPath:(IGRowPath *)path
{
   NSString* imgName = (path.rowIndex == 0) ? @"Bookcase_Universal_toptall.png" : @"Bookcase_Universal_white_withBG.png";

   UIView* container = [[UIView alloc]init];
   UIImageView* iv = [[UIImageView alloc]initWithImage:[UIImage imageNamed:imgName]];
   iv.contentMode = UIViewContentModeScaleToFill;

   iv.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
   iv.frame = CGRectMake(0, -5, 0, 0);
  [container addSubview:iv];
   
   return container;
}

The first line in each method simply detects if we’re providing information for the first row, or any other row. This is only important if you’re shelve images have shadows associated with them. As the first row shouldn’t have a shadow.

Now if you run the project, you should see this:

And just like that, we’re done!

I’ve included the project complete with all the images for the shelves here.

If you can't wait for my next post, you can grab the free NucliOS app in the app store to get an idea of the power of this product. 

Enjoy!

By Stephen Zaharuk (SteveZ)