preface

Recently read www.raywenderlich.com about the UICollectionView custom layout tutorial, make a note, easy to refer to later. See WWDC 2012 Session Notes 219 Advanced Collection Views and Building Custom Layouts for an overview of UICollectionView Custom Layouts.

The basic principle of

The yellow area is the mobile phone screen, and the blue rounded rectangle is UICollectionViewCells. The cell takes RADIUS as its radius, rotates around a point outside the mobile phone screen as its center, and the included Angle between each cell is anglePerItem.

As you can see, not all cells are in the screen. Assuming that the rotation Angle of the 0th cell is Angle, then the rotation Angle of the 1st cell is Angle + anglePerItem, and so on to get the rotation Angle of the ith cell:

CGFloat  angleFori = angle + anglePerItem *i
Copy the code

The Angle coordinates are as follows: 0° represents the center of the screen, the left is the negative direction, and the right is the positive direction. Therefore, when the Angle of the cell is 0°, it is vertically centered.

Custom layout properties

Because the system UICollectionViewLayoutAttributes no Angle and anchorPoint property, so we inherit UICollectionViewLayoutAttributes custom layout properties.

@implementation WheelCollectionLayoutAttributes
- (instancetype)init{
    self = [super init];
    if(self) {self.anchorPoint = CGPointMake(0.5, 0.5); self.angle = 0; }return self;
}

- (id)copyWithZone:(NSZone *)zone{
    WheelCollectionLayoutAttributes *attribute = [super copyWithZone:zone];
    attribute.anchorPoint = self.anchorPoint;
    attribute.angle = self.angle;
    return attribute;
}
@end
Copy the code

Initialize anchorPoint to CGPointMake(0.5, 0.5), Angle to 0, and override – (id)copyWithZone:(NSZone *)zone when the object is copied, Ensure that the anchorPoint and Angle attributes are assigned.

** for custom layout properties, UICollectionViewCell must implement the following 👇 method to change the anchorPoint of the cell. Changing the anchorPoint causes the position of the layer to change. See this article for details on the relationship between anchorPoint and position.

- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes{ [super applyLayoutAttributes:layoutAttributes]; / / change the anchor point (change the anchor point will affect the position of the center reference http://www.jianshu.com/p/15f007f40209) WheelCollectionLayoutAttributes * attribute = (WheelCollectionLayoutAttributes*)layoutAttributes; self.layer.anchorPoint = attribute.anchorPoint; CGFloat centerY = (attribute.anchorpoint.y-0.5)*CGRectGetHeight(self.bounds); self.center = CGPointMake(self.center.x, centerY + self.center.y); }Copy the code

Custom layout

  1. Initialize first: 👇 this method tellsCollectionViewLayout attributes are customWheelCollectionLayoutAttributesProperty instead of using the system defaultUICollectionViewLayoutAttributesProperties.
+ (Class)layoutAttributesClass{
    return [WheelCollectionLayoutAttributes class];
}
Copy the code
- (instancetype)init{
    self = [super init];
    if(self) {/* circle radius */ self.radius = 500.0; /**itemCell size */ self.itermSize = CGSizeMake(133, 173); Self.angleperitem = atan(self.itermsize.width/self.radius); self.anglePerItem = atan(self.itermsize.width/self.radius); }return self;
}
Copy the code
  1. - (void)prepareLayoutCalled when the Collection View is first displayed on the screen, this method initializes and saves the layout properties.
- (void)prepareLayout{
    [super prepareLayout];
    [self.allAttributeArray removeAllObjects];
    NSUInteger numberOfItem = [self.collectionView numberOfItemsInSection:0];
    if (numberOfItem == 0) {
        return; } /* Get the total rotation Angle */ CGFloat angleAtExtreme = (numberOfItem - 1) * self.anglePerItem; /* As the UICollectionView moves, 0 a cell at the initial point of * / CGFloat Angle angleAtExtreme = 1 * * self. CollectionView. ContentOffset. / x (self.collectionView.contentSize.width - CGRectGetWidth(self.collectionView.bounds)); / * current * the coordinates of the center of the screen/CGFloat centerX = self. CollectionView. ContentOffset. X + CGRectGetWidth (self. CollectionView. Bounds) / 2.0; / * the location of the anchor point * / CGFloat anchorPointY = (self. ItermSize. Height 2.0 + / self. The radius)/self itermSize. Height;for (int i = 0; i < numberOfItem; i++) {
        WheelCollectionLayoutAttributes *attribute = [WheelCollectionLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]; Attribute. AnchorPoint = CGPointMake (0.5, anchorPointY); attribute.size = self.itermSize; attribute.center = CGPointMake(centerX, CGRectGetMidY(self.collectionView.bounds)); attribute.angle = angle + self.anglePerItem *i; attribute.transform = CGAffineTransformMakeRotation(attribute.angle); attribute.zIndex = (int)(-1) *i *1000; [self.allAttributeArray addObject:attribute]; }}Copy the code

By the state of the starting point and end point, when the self. The collectionView. ContentOffset. X = 0, 0 a cell is 0, the Angle of rotation. When the self collectionView. The contentOffset. X = Max, The maximum rotation Angle of cell 0 is (numberofitem-1) * self.anglePerItem.

CGFloat angle = -1 *angleAtExtreme * self.collectionView.contentOffset.x / (self.collectionView.contentSize.width - CGRectGetWidth(self.collectionView.bounds));
Copy the code

So the rotation Angle of cell I is:

attribute.angle = angle + self.anglePerItem *i;
Copy the code

The anchor point of the cell can be calculated from the figure above. The value in the X-axis direction remains unchanged, while the value in the Y-axis direction changes:

CGFloat anchorPointY = (self. ItermSize. Height 2.0 + / self. The radius)/self itermSize. Height; CGFloat anchorPoint = CGPointMake(0.5, anchorPointY);Copy the code

(3) – (CGSize) collectionViewContentSize return collectionView content size

- (CGSize)collectionViewContentSize{
    CGFloat numberOfIterm = [self.collectionView numberOfItemsInSection:0];
    return CGSizeMake(numberOfIterm *self.itermSize.width, CGRectGetHeight(self.collectionView.bounds));
}
Copy the code

4. Return the layout properties

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    return self.allAttributeArray;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    return self.allAttributeArray[indexPath.item];
}
Copy the code
  1. whencollectionViewAs I slide, I rearrange the collectionView
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}
Copy the code

Finally, you can download the Demo from GitHub.