Skip to content

Instantly share code, notes, and snippets.

@leeprobert
Created March 15, 2013 11:11
Show Gist options
  • Save leeprobert/5169093 to your computer and use it in GitHub Desktop.
Save leeprobert/5169093 to your computer and use it in GitHub Desktop.
UICollectionViewFlowLayout sub-class that creates layout attributes for items so they render like a carousel. The point of view is from the center of the carousel so is suited for lots of items. It also changes its behaviour based on orientation.
//
// CollectionViewCarouselFlowLayout.m
// iP2
//
// Created by Lee Probert on 07/03/2013.
//
//
#import "CollectionViewCarouselFlowLayout.h"
#define ITEM_SIZE 300.0f
#define ACTIVE_DISTANCE 200
#define ZOOM_FACTOR 0.2
#define PADDING 20
#define MAX_ANGLE 80
#define PERSPECTIVE 1.0/-400
@interface CollectionViewCarouselFlowLayout (){
UIInterfaceOrientation _orientation;
}
-(void)initLayoutProperties;
@end
@implementation CollectionViewCarouselFlowLayout
-(void)prepareLayout {
[super prepareLayout];
[self initLayoutProperties];
}
-(void)initLayoutProperties {
_orientation = [[UIApplication sharedApplication] statusBarOrientation];
self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
self.scrollDirection = UIInterfaceOrientationIsPortrait(_orientation)? UICollectionViewScrollDirectionVertical : UICollectionViewScrollDirectionHorizontal;
self.sectionInset = UIInterfaceOrientationIsPortrait(_orientation)? UIEdgeInsetsMake(0, 200, 0, 200) : UIEdgeInsetsMake(200, 0, 200, 0);
self.minimumLineSpacing = PADDING;
self.minimumInteritemSpacing = PADDING;
}
/*
Rotate in 3D based on position on screen
*/
-(NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray* array = [super layoutAttributesForElementsInRect:rect];
CGRect visibleRect;
visibleRect.origin = self.collectionView.contentOffset;
visibleRect.size = self.collectionView.bounds.size;
int debug = 1;
for(UICollectionViewLayoutAttributes* attributes in array) {
if(CGRectIntersectsRect(attributes.frame, rect)){
CGFloat d = UIInterfaceOrientationIsPortrait(_orientation)?
CGRectGetMidY(visibleRect)-attributes.center.y :
CGRectGetMidX(visibleRect)-attributes.center.x;
CGFloat w = visibleRect.size.width;
CGFloat h = visibleRect.size.height;
CGFloat dRatio = UIInterfaceOrientationIsPortrait(_orientation)? d/(h/2) : d/(w/2);
CGFloat angle = MAX_ANGLE*dRatio; // an angle between 0 and MAX_ANGLE based on proximity to center
CGFloat radians = DEGREES_TO_RADIANS(angle);
debug = 0;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = PERSPECTIVE;
rotationAndPerspectiveTransform = UIInterfaceOrientationIsPortrait(_orientation)?
CATransform3DRotate(rotationAndPerspectiveTransform, radians, 1.0f, 0.0f, 0.0f) :
CATransform3DRotate(rotationAndPerspectiveTransform, radians, 0.0f, 1.0f, 0.0f);
attributes.transform3D = rotationAndPerspectiveTransform;
}
}
return array;
}
/*
Scale based on position on screen
-(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 distance = CGRectGetMidX(visibleRect)-attributes.center.x;
CGFloat normalizedDistance = distance/ACTIVE_DISTANCE;
if(ABS(distance)<ACTIVE_DISTANCE){
CGFloat zoom = 1+ZOOM_FACTOR*(1-ABS(normalizedDistance));
attributes.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0);
attributes.zIndex = round(zoom);
}
}
}
return array;
}
*/
-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
CGFloat offsetAdjustment = MAXFLOAT;
CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds)/2.0);
CGFloat verticalCenter = proposedContentOffset.y + (CGRectGetHeight(self.collectionView.bounds)/2.0);
CGRect targetRectHorizontal = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
CGRect targetRectVertical = CGRectMake(0.0, proposedContentOffset.y, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
NSArray *array = UIInterfaceOrientationIsPortrait(_orientation)?
[super layoutAttributesForElementsInRect:targetRectVertical] :
[super layoutAttributesForElementsInRect:targetRectHorizontal];
for(UICollectionViewLayoutAttributes* layoutAttributes in array) {
CGFloat itemHorizontalCenter = layoutAttributes.center.x;
CGFloat itemVerticalCenter = layoutAttributes.center.y;
if(UIInterfaceOrientationIsPortrait(_orientation)){
if(ABS(itemVerticalCenter-verticalCenter) < ABS(offsetAdjustment)){
offsetAdjustment = itemVerticalCenter-verticalCenter;
}
}else{
if(ABS(itemHorizontalCenter-horizontalCenter) < ABS(offsetAdjustment)){
offsetAdjustment = itemHorizontalCenter-horizontalCenter;
}
}
}
return UIInterfaceOrientationIsPortrait(_orientation)?
CGPointMake(proposedContentOffset.x, proposedContentOffset.y+offsetAdjustment) :
CGPointMake(proposedContentOffset.x+offsetAdjustment, proposedContentOffset.y);
}
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return YES;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment