UICollectionView in iOS 6, Custom Layouts: Part 2/2

Audio : Listen to This Blog.

Recap : Before going to the next part, we will recap what we learned in part 1 of this blog. We learned about UICollectionView , custom UICollectionViewCell & supplementary view concepts. And we had implemented sample using this concepts.
UICollection view support layout for positioning all the views independently. The layout implementation is responsible for creating the layout attributes for every item in the UICollectionView.
We can either use the built-in UICollectionViewFlowLayout or custom layout by inheriting from UICollectionViewLayout.
In this we will see how to create custom layout and adding gesture recognizer to the cells.
For creating custom layout, you need to have implementation of

  • prepareLayout
  • -(UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
  • -(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
  • -(CGSize)collectionViewContentSize

Rather than using UICollectionViewFlowLayout, we can subclass it for further customized layout. Here can be used to create a layout that does not wrap the Cells into a grid, but instead creates a single row with a horizontal scrolling effect. As shown below image.
iOS Simulator Screen shot Mar 22, 2013 5.58.07 PM
First step is to create project with EmptyApplication. I have named my project as CollectionViewCustomLayoutDemo. Then you create a new file by clicking right on the project named MainViewController subclassing UICollectionViewController, mark both the checkboxes i.e. With xib and and targeted for iPad.
So once its done ,the next step is to open the nib file and delete the already existing view. Then drag and drop the UICollectionViewController.
Create a subclass of UICollectionViewCell named CollectionViewCell as done in the previous sample.
Now create one more file named CollectionViewLayout subclassing UICollectionViewLayout.
Implement the CollectionViewLayout.h/.m files
1) Initializing any layout properties that apply to the layout
-(id)init{
self = [super init];
if(self){
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIEdgeInsetsMake(200, 0.0, 200, 0.0);
self.minimumLineSpacing = 10.0;
self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
}
return self;
}

  • Here we are setting scrollDirection for collection view , either we can set to UICollectionViewScrollDirectionHorizontal or UICollectionViewScrollDirectionVertical. But in our sample we need UICollectionViewScrollDirectionHorizontal scrollDirection.
  • To provide some space around the UICollectionView, layouts have a sectionInset property of type UIEdgeInsets. Example: UIEdgeInsetsMake(200, 0.0, 200, 0.0); provides 200 pixels space around each cell.
  • The minimum spacing to use between lines of items in the grid. flow layout uses the value in this property to set the spacing between lines in a section.Example:self.minimumLineSpacing = 10.0; here we are providing 10 pixels space between items.
  • To provide CGSize for each item in collection view, layout have itemSize property of type CGSizeMake. The flow layout uses the value in this property to set the size of each cell.

2) Overriding layoutAttributesForElementsInRect to return an array of UICollectionViewLayoutAttributes. Each UICollectionViewLayoutAttribute contains information on how to layout the particular item, including properties such as its Center, Size , Zindex & tansform3D etc. It provide the information to the collection view regarding how to arrange each item.
-(NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
NSArray *array = [super layoutAttributesForElementsInRect:rect];

CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;

for(UICollectionViewLayoutAttributes *attributes in array){
if(CGRectIntersectsRect(attributes.frame, rect)){
CGFloat dist = CGRectGetMidX(visibleRect) - attributes.center.x;
CGFloat normalizedDist = dist/ACTIVE_DISTANCE;
If(ABS(dist)<ACTIVE_DISTANCE){
CGFloat zoom = 1 + ZOOM_FACTOR*(1 - ABS(normalizedDist));

attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0);
attributes.zIndex = round(zoom);
}
}
}
return array;
}

3) Overriding ShouldInvalidateLayoutForBoundsChange, returning true so that when bounds of the UICollectionView changes, the layout of the cells will be recalculated.
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
}

In AppDelegate import both the files MainViewController and CollectionViewLayout. Create properties for both. And then follow as below
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

self.viewLayout = [[CollectionViewLayout alloc]init];
self.viewController = [[MainViewController alloc]initWithCollectionViewLayout:self.viewLayout];
self.window.rootViewController = self.viewController;
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}

In MainViewController implement the data source methods as before and run your project..
this was just an example of CustomLayout.
Now we will add gesture recognizer to the collectionView. In MainViewController, in viewDidLoad() add the following lines
//adding gesture recognizer to the collectionView
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleGesture:)];
self.collectionView.userInteractionEnabled = YES;
[self.collectionView addGestureRecognizer:gestureRecognizer];

and then you can implement handleGesture() method for every tap on collection view. Here in our sample, if index path is null, we are adding cell for each tap on collection view else remove selected index path element.
-(void)handleGesture:(UITapGestureRecognizer *)sender{
//deleting and adding cells
if(sender.state == UIGestureRecognizerStateEnded){
// get selected point in collection view
CGPoint initialPoint = [sender locationInView:self.collectionView];
// check Index path for item at users tap point
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:initialPoint];
// if item exists @ tapped point, will have index path value. Delete tapped item, which exist at tapped point.
// if item not exist at tapped point, index path value will be null . Then increase cell count to +1 & add new item at 0th index path .
if(nil != indexPath){
self.cellCount = self.cellCount-1;

[self.collectionView performBatchUpdates:^{[self.collectionView deleteItemsAtIndexPaths:[NSArray arrayWithObject:indexPath]];} completion:nil];
}
else{
self.cellCount = self.cellCount+1;
[self.collectionView performBatchUpdates:^{[self.collectionView insertItemsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForItem:0 inSection:0]]];} completion:nil];
}
}
}
Here we are using
- (void)performBatchUpdates:(void (^)(void))updates completion:(void (^)(BOOL finished))completion
We can use this method in cases where you want to insert, delete, reload or move cells around the collection view in one single animated operation, as opposed to in several separate animations. Use the blocked passed in the updates parameter to specify all of the operations you want to perform.
Here is the sample code.CollectionViewCustomLayoutDemo
Conclusion :
This blog helps to learn about collection view concept. How to implement collection view using custom UICollectionviewcell , custom layout & supplementary view concepts. And you can find the sample demo regarding these concepts.
So guys that was a small journey of UICollectionView. I hope you enjoyed it.

Read Similar Blogs