UICollectionView Demo

1、

利用系统自动布局UICollectionViewFlowLayout进行布局。

ViewController1

#import "ViewController1.h"

@interface ViewController1 ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
{
    UICollectionView * _collectionView;
}
@end

@implementation ViewController1

- (void)viewDidLoad {
    [super viewDidLoad];
    UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    layout.itemSize = CGSizeMake(200, 100);
    _collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
    _collectionView.delegate = self;
    _collectionView.dataSource = self;
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CellId"];
    [self.view addSubview:_collectionView];
}

#pragma mack - collection delegate

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 50;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%225/225.0 green:arc4random()%225/225.0 blue:arc4random()%225/225.0 alpha:1];
    return cell;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
View Code

2、

继承系统自动布局UICollectionViewFlowLayout。

布局FlowLayout2

#import <UIKit/UIKit.h>

@interface FlowLayout2 : UICollectionViewFlowLayout
@property (nonatomic, assign) NSInteger itemCount;
@end
View Code
#import "FlowLayout2.h"

@interface FlowLayout2()
{
    NSMutableArray * _attributeArray;
}
@end

@implementation FlowLayout2
- (void)prepareLayout{
    [super prepareLayout];
    _attributeArray = [NSMutableArray array];
    CGFloat kWidth = ([UIScreen mainScreen].bounds.size.width - self.sectionInset.left - self.sectionInset.right - self.minimumInteritemSpacing)/2;
    
    CGFloat kHeight[2] = {self.sectionInset.top, self.sectionInset.bottom};
    
    for (int i = 0; i < _itemCount; i++) {
        NSIndexPath * index = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index];
        
        CGFloat rHeight = arc4random()%150+40;
        NSInteger rWidth = 0;
        
        if (kHeight[0] < kHeight[1]) {
            kHeight[0] = kHeight[0] + rHeight + self.minimumLineSpacing;
            rWidth = 0;
        }else{
            kHeight[1] = kHeight[1] + rHeight + self.minimumLineSpacing;
            rWidth = 1;
        }
        
        attributes.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + kWidth)*rWidth, kHeight[rWidth] - rHeight - self.minimumLineSpacing, kWidth, rHeight);
        [_attributeArray addObject:attributes];
    }
    
    if (kHeight[0] > kHeight[1]) {
        self.itemSize = CGSizeMake(kWidth, (kHeight[0] - self.sectionInset.top)*2/_itemCount - self.minimumLineSpacing);
    }else{
        self.itemSize = CGSizeMake(kWidth, (kHeight[1] - self.sectionInset.top)*2/_itemCount - self.minimumLineSpacing);
    }
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    return _attributeArray;
}
@end
View Code

控制器UIViewController

#import "ViewController2.h"
#import "FlowLayout2.h"

@interface ViewController2 ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate>
{
    UICollectionView * _collectionView;
}
@end

@implementation ViewController2

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    FlowLayout2 * layout = [[FlowLayout2 alloc] init];
    layout.itemCount = 100;
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    layout.itemSize = CGSizeMake(100, 100);
    
    _collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
    _collectionView.delegate = self;
    _collectionView.dataSource = self;
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CellId"];
    [self.view addSubview:_collectionView];
}

#pragma mark - collection dataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 100;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"%ld",indexPath.item);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
View Code

3、

 

继承系统UICollectionViewLayout

#import <UIKit/UIKit.h>

@interface FlowLayout3 : UICollectionViewLayout
@property (nonatomic,assign) int itemCount;
@end
View Code
#import "FlowLayout3.h"

@interface FlowLayout3()
{
    NSMutableArray * _attributeArray;
}
@end

@implementation FlowLayout3
- (void)prepareLayout{
    _itemCount = (int)[self.collectionView numberOfItemsInSection:0];
    _attributeArray = [NSMutableArray array];
    CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height)/2;
    CGPoint center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2);
    for (int i = 0; i < _itemCount; i++) {
        UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
        attributes.size = CGSizeMake(50, 50);
        CGFloat x = center.x + cosf(2*M_PI/_itemCount*i)*(radius-25);
        CGFloat y = center.y + sinf(2*M_PI/_itemCount*i)*(radius-25);
        attributes.center = CGPointMake(x, y);
        [_attributeArray addObject:attributes];
    }
}

-(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    return _attributeArray;
}

- (CGSize)collectionViewContentSize{
    return self.collectionView.frame.size;
}
View Code

UIViewController

#import "ViewController3.h"
#import "FlowLayout3.h"
@interface ViewController3 ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
{
    UICollectionView * _collectionView;
}
@end

@implementation ViewController3

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    FlowLayout3 * layout = [[FlowLayout3 alloc] init];
    _collectionView = [[UICollectionView alloc] initWithFrame:self.view.frame collectionViewLayout:layout];
    _collectionView.delegate = self;
    _collectionView.dataSource = self;
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CellId"];
    [self.view addSubview:_collectionView];
}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 20;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    cell.layer.masksToBounds = YES;
    cell.layer.cornerRadius = 25;
    return cell;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
View Code

4、

UICollectionViewLayout

#import <UIKit/UIKit.h>

@interface FlowLayout4 : UICollectionViewLayout

@end
View Code
#import "FlowLayout4.h"

@implementation FlowLayout4

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    //创建一个item布局属性类
    UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    //获取item的个数
    int itemCounts = (int)[self.collectionView numberOfItemsInSection:0];
    //设置每个item的大小为260*100
    attributes.size = CGSizeMake(320, 100);
    
    attributes.center = CGPointMake(self.collectionView.frame.size.width/2, self.collectionView.frame.size.height/2 + self.collectionView.contentOffset.y);
    CATransform3D tran3d = CATransform3DIdentity;
    tran3d.m34 = -1/2000.0;
    CGFloat radius = 50/tanf(M_PI*2/itemCounts/2);
    //     CGFloat angle = (float)(indexPath.row)/itemCounts*M_PI*2;
    //获取当前的偏移量
    float offset = self.collectionView.contentOffset.y;
    //在角度设置上,添加一个偏移角度
    float angleOffset = offset/self.collectionView.frame.size.height;
    CGFloat angle = (float)(indexPath.row + angleOffset - 1)/itemCounts*M_PI*2;
    tran3d = CATransform3DRotate(tran3d, angle, 1.0, 0, 0);
    tran3d = CATransform3DTranslate(tran3d, 0, 0, radius);
    //进行设置
    attributes.transform3D = tran3d;
    return attributes;
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSMutableArray * attributes = [[NSMutableArray alloc] init];
    //遍历设置每个item的布局属性
    for (int i=0; i < [self.collectionView numberOfItemsInSection:0]; i++) {
        [attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
    }
    return attributes;
}

- (CGSize)collectionViewContentSize{
    return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height * ([self.collectionView numberOfItemsInSection:0] + 2));
}

//返回yes,则一有变化就会刷新布局
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}

- (void)prepareLayout{

}
View Code

UIViewController

#import "ViewController4.h"
#import "FlowLayout4.h"

@interface ViewController4 ()<UICollectionViewDelegateFlowLayout, UICollectionViewDataSource>
{
    UICollectionView * _collectionView;
    NSInteger i;
}
@end

@implementation ViewController4

- (void)viewDidLoad {
    [super viewDidLoad];
    i = 100;
    FlowLayout4 * layout = [[FlowLayout4 alloc] init];
    _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) collectionViewLayout:layout];
    _collectionView.delegate = self;
    _collectionView.dataSource = self;
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CellId"];
    [self.view addSubview:_collectionView];
    _collectionView.center = self.view.center;
    
    [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(chageContentOffset) userInfo:nil repeats:YES];
}

-(void)chageContentOffset{
    i = i + 100;
    _collectionView.contentOffset = CGPointMake(0 , i);
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 10;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell  = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 250, 80)];
    label.text = [NSString stringWithFormat:@"我是第%ld行",(long)indexPath.row];
    [cell.contentView addSubview:label];
    return cell;
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
    //小于半屏 则放到最后一屏多半屏
    if (scrollView.contentOffset.y < 200) {
        scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y+10*400);
        //大于最后一屏多一屏 放回第一屏
    }else if(scrollView.contentOffset.y > 11 * 400){
        scrollView.contentOffset = CGPointMake(0, scrollView.contentOffset.y-10*400);
    }
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
View Code

5、

UICollectionViewLayout

#import "FlowLayout5.h"

@implementation FlowLayout5

- (void)prepareLayout{
    [super prepareLayout];
}

- (CGSize)collectionViewContentSize{
    return CGSizeMake(self.collectionView.frame.size.width*([self.collectionView numberOfItemsInSection:0]+2), self.collectionView.frame.size.height*([self.collectionView numberOfItemsInSection:0]+2));
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    return YES;
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSMutableArray * attributes = [NSMutableArray array];
    for (int i = 0; i < [self.collectionView numberOfItemsInSection:0]; i++) {
        //遍历设置每个item的布局属性
        [attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]]];
    }
    return attributes;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    NSInteger itemCount = [self.collectionView numberOfItemsInSection:0];
    attributes.center = CGPointMake(self.collectionView.frame.size.width/2+self.collectionView.contentOffset.x, self.collectionView.frame.size.height/2+self.collectionView.contentOffset.y);
    attributes.size = CGSizeMake(30, 30);
    CATransform3D trans3D = CATransform3DIdentity;
    trans3D.m34 = -1/900.0;
    CGFloat radius = 15/tanf(M_PI*2/itemCount/2);
    
    //根据偏移量,改变角度
    //添加了一个x的偏移量
    CGFloat offsetY = self.collectionView.contentOffset.y;
    CGFloat offsetX = self.collectionView.contentOffset.x;
    
    //分别计算偏移的角度
    CGFloat angleOffsetY = offsetY/self.collectionView.frame.size.height;
    CGFloat angleOffsetX = offsetX/self.collectionView.frame.size.width;
    
    //x,y的默认方向相反
    CGFloat angleY = (indexPath.item + angleOffsetY - 1)/itemCount * M_PI*2;
    CGFloat angleX = (indexPath.item + angleOffsetX - 1)/itemCount * M_PI*2;
    
    //四个方向的排列
    if (indexPath.item % 4 == 1) {
        trans3D = CATransform3DRotate(trans3D, angleY, 1.0, 0, 0);
    }else if (indexPath.row % 4 == 2){
        trans3D = CATransform3DRotate(trans3D, angleX, 0, 1.0, 0);
    }else if (indexPath.row % 4 == 3){
        trans3D = CATransform3DRotate(trans3D, angleY, 0.5, 0.5, 0);
    }else{
        trans3D = CATransform3DRotate(trans3D, angleY, 0.5, -0.5, 0);
    }
    
    trans3D = CATransform3DTranslate(trans3D, 0, 0, radius);
    attributes.transform3D = trans3D;
    return attributes;
}
@end
View Code

UIViewController

#import "ViewController5.h"
#import "FlowLayout5.h"

@interface ViewController5 ()<UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
{
    UICollectionView * _collectionView;
}
@end

@implementation ViewController5

- (void)viewDidLoad {
    [super viewDidLoad];
    FlowLayout5 * layout = [[FlowLayout5 alloc] init];
    _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 300, 300) collectionViewLayout:layout];
    _collectionView.delegate = self;
    _collectionView.dataSource = self;
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"CellId"];
    [self.view addSubview:_collectionView];
    _collectionView.center = self.view.center;
    _collectionView.backgroundColor = [UIColor whiteColor];
    [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(chageContentOffset) userInfo:nil repeats:YES];
}

//这里设置的偏移量是为了无缝进行循环的滚动
-(void)chageContentOffset{
    _collectionView.contentOffset = CGPointMake(arc4random()%(11*300), arc4random()%(10*300));
}

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 30;
}

-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewCell * cell  = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    cell.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1];
    UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 30, 30)];
    label.text = [NSString stringWithFormat:@"%ld",(long)indexPath.row];
    [cell.contentView addSubview:label];
    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    NSLog(@"%ld====row",indexPath.row);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
View Code

6、

UIViewController中,布局FlowLayout6继承UICollectionViewFlowLayout,FlowLayout_6继承布局FlowLayout6。

#import "ViewController6.h"
#import "CollectionViewCell6.h"
#import "FlowLayout_6.h"

@interface ViewController6 ()<CollectionViewResetFlowLayoutDelegate, CollectionViewResetFlowLayoutDataSource>

@property (weak, nonatomic) IBOutlet UICollectionView * collectionView;
@property (strong, nonatomic) NSMutableArray * photosArray;
@end

@implementation ViewController6

- (void)viewDidLoad {
    [super viewDidLoad];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self setupPhotosArray];
}

- (IBAction)refreshUI:(UIButton *)sender {
    [self setupPhotosArray];
    [self.collectionView reloadData];
}

- (void)setupPhotosArray{
    [_photosArray removeAllObjects];
    _photosArray = nil;
    _photosArray = [NSMutableArray array];
    for (NSInteger i = 1; i <= 21; i++) {
        NSString * photoName = [NSString stringWithFormat:@"%ld.jpg",i];
        UIImage * photoImg = [UIImage imageNamed:photoName];
        [_photosArray addObject:photoImg];
    }
}


#pragma mark - UICollectionView Delegate

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 2;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    if (section == 0) {
        return 1;
    }
    return _photosArray.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        static NSString * cellID = @"headerCell";
        UICollectionViewCell * cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
        return cell;
    }else {
        static NSString * cellID = @"CellId";
        CollectionViewCell6 * cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
        [cell.imageView removeFromSuperview];
        cell.imageView.frame = cell.bounds;
        cell.imageView.image = _photosArray[indexPath.item];
        [cell.contentView addSubview:cell.imageView];
        return cell;
    }
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        return;
    }
    if (_photosArray.count == 1) {
        return;
    }
    [self.collectionView performBatchUpdates:^{
        //在执行完performBatchUpdates操作之后,collection view会自动reloadData,调用numberOfItemsInSection等方法重新布局,这时就会出现数据越界等情况,所以我们要在performBatchUpdates的block中更新numberOfItemsInSection的个数.
        [_photosArray removeObjectAtIndex:indexPath.item];
        
        [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
    } completion:^(BOOL finished) {
        [self.collectionView reloadData];
    }];
}


#pragma mark - CollectionViewFlowLayoutDelegate
- (CGFloat)sectionSpacingForCollectionView:(UICollectionView *)collectionView {
    return 5.f;
}

- (CGFloat)minimumInteritemSpacingForCollectionView:(UICollectionView *)collectionView {
    return 5.f;
}

- (CGFloat)minimumLineSpacingForCollectionView:(UICollectionView *)collectionView {
    return 5.f;
}

- (UIEdgeInsets)insetsForCollectionView:(UICollectionView *)collectionView {
    return UIEdgeInsetsMake(5.f, 0, 5.f, 0);
}

- (CGSize)collectionView:(UICollectionView *)collectionView sizeForLargeItemsInSection:(NSInteger)section {
    if (section == 0) {
        return CGSizeMake(320, 200);
    }
    return CGSizeZero; //same as default !
}

#pragma mark - CollectionViewResetFlowLayoutDelegate

- (UIEdgeInsets)autoScrollTrigerEdgeInsets:(UICollectionView *)collectionView {
    return UIEdgeInsetsMake(50.f, 0, 50.f, 0); //Sorry, horizontal scroll is not supported now.
}

- (UIEdgeInsets)autoScrollTrigerPadding:(UICollectionView *)collectionView {
    return UIEdgeInsetsMake(64.f, 0, 0, 0);
}

- (CGFloat)reorderingItemAlpha:(UICollectionView *)collectionview {
    return .3f;
}

- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath {
    [self.collectionView reloadData];
}

#pragma mark - CollectionViewResetFlowLayoutDelegate

- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath didMoveToIndexPath:(NSIndexPath *)toIndexPath {
    UIImage * image = [_photosArray objectAtIndex:fromIndexPath.item];
    [_photosArray removeObjectAtIndex:fromIndexPath.item];
    [_photosArray insertObject:image atIndex:toIndexPath.item];
}

- (BOOL)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath {
    if (toIndexPath.section == 0) {
        return NO;
    }
    return YES;
}

- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.section == 0) {
        return NO;
    }
    return YES;
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
View Code

UICollectionViewCell

#import <UIKit/UIKit.h>

@interface CollectionViewCell6 : UICollectionViewCell
@property (nonatomic, strong) UIImageView * imageView;
@end
View Code
#import "CollectionViewCell6.h"

@implementation CollectionViewCell6

- (instancetype)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {
        _imageView = [[UIImageView alloc] init];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
    }
    return self;
}

- (void)setBounds:(CGRect)bounds{
    [super setBounds:bounds];
    self.imageView.frame = bounds;
}

- (void)setHighlighted:(BOOL)highlighted{
    [super setHighlighted:highlighted];
    if (highlighted) {
        _imageView.alpha = .7f;
    }else{
        _imageView.alpha = 1.f;
    }
}

@end
View Code

FlowLayout6

#import <UIKit/UIKit.h>

@protocol CollectionViewFlowLayoutDelegate<UICollectionViewDelegateFlowLayout>
@optional
- (CGSize)collectionView:(UICollectionView *)collectionView sizeForLargeItemsInSection:(NSInteger)section;
- (UIEdgeInsets)insetsForCollectionView:(UICollectionView *)collectionView;
- (CGFloat)sectionSpacingForCollectionView:(UICollectionView *)collectionView;
- (CGFloat)minimumInteritemSpacingForCollectionView:(UICollectionView *)collectionView;
- (CGFloat)minimumLineSpacingForCollectionView:(UICollectionView *)collectionView;
@end

@protocol CollectionViewFlowLayoutDatasource <UICollectionViewDataSource>

@end


@interface FlowLayout6 : UICollectionViewFlowLayout
@property (nonatomic, assign, readonly) CGSize largeCellSize;
@property (nonatomic, assign, readonly) CGSize smallCellSize;
- (CGFloat)contentHeight;

@property (nonatomic, weak) id<CollectionViewFlowLayoutDelegate> delegate;
@property (nonatomic, weak) id<CollectionViewFlowLayoutDatasource> datasource;
@end
View Code
#import "FlowLayout6.h"

@interface FlowLayout6()
@property (nonatomic, assign) NSInteger numberOfCells;
@property (nonatomic, assign) CGFloat numberOfLines;
@property (nonatomic, assign) CGFloat itemSpacing;
@property (nonatomic, assign) CGFloat lineSpacing;
@property (nonatomic, assign) CGFloat sectionSpacing;
@property (nonatomic, assign) CGSize collectionViewSize;
@property (nonatomic, assign) UIEdgeInsets insets;
@property (nonatomic, assign) CGRect oldRect;
@property (nonatomic, strong) NSArray * oldArray;
@property (nonatomic, strong) NSMutableArray * largeCellSizeArray;
@property (nonatomic, strong) NSMutableArray * smallCellSizeArray;
@end

@implementation FlowLayout6
- (void)prepareLayout{
    [super prepareLayout];
    _collectionViewSize = self.collectionView.bounds.size;
    _itemSpacing = 0;
    _lineSpacing = 0;
    _sectionSpacing = 0;
    _insets = UIEdgeInsetsMake(0, 0, 0, 0);
    if ([self.delegate respondsToSelector:@selector(minimumInteritemSpacingForCollectionView:)]) {
        _itemSpacing = [self.delegate minimumInteritemSpacingForCollectionView:self.collectionView];
    }
    if ([self.delegate respondsToSelector:@selector(minimumLineSpacingForCollectionView:)]) {
        _lineSpacing = [self.delegate minimumLineSpacingForCollectionView:self.collectionView];
    }
    if ([self.delegate respondsToSelector:@selector(sectionSpacingForCollectionView:)]) {
        _sectionSpacing = [self.delegate sectionSpacingForCollectionView:self.collectionView];
    }
    if ([self.delegate respondsToSelector:@selector(insetsForCollectionView:)]) {
        _insets = [self.delegate insetsForCollectionView:self.collectionView];
    }
}

- (CGFloat)contentHeight{
    CGFloat contentHeight = 0;
    NSInteger numberOfSections = self.collectionView.numberOfSections;
    CGSize collectionViewSize = self.collectionView.bounds.size;
    
    UIEdgeInsets insets = UIEdgeInsetsZero;
    if ([self.delegate respondsToSelector:@selector(insetsForCollectionView:)]) {
        insets = [self.delegate insetsForCollectionView:self.collectionView];
    }
    CGFloat sectionSpacing = 0;
    if ([self.delegate respondsToSelector:@selector(sectionSpacingForCollectionView:)]) {
        sectionSpacing = [self.delegate sectionSpacingForCollectionView:self.collectionView];
    }
    CGFloat itemSpacing = 0;
    if ([self.delegate respondsToSelector:@selector(minimumInteritemSpacingForCollectionView:)]) {
        itemSpacing = [self.delegate minimumInteritemSpacingForCollectionView:self.collectionView];
    }
    CGFloat lineSpacing = 0;
    if ([self.delegate respondsToSelector:@selector(minimumLineSpacingForCollectionView:)]) {
        lineSpacing = [self.delegate minimumLineSpacingForCollectionView:self.collectionView];
    }
    
    contentHeight += insets.top + insets.bottom + sectionSpacing * (numberOfSections - 1);
    
    CGFloat lastSmallCellHeight = 0;
    for (NSInteger i = 0; i < numberOfSections; i++) {
        NSInteger numberOfLines = ceil((CGFloat)[self.collectionView numberOfItemsInSection:i] / 3.f);
        
        CGFloat largeCellSideLength = (2.f * (collectionViewSize.width - insets.left - insets.right) - itemSpacing) / 3.f;
        CGFloat smallCellSideLength = (largeCellSideLength - itemSpacing) / 2.f;
        CGSize largeCellSize = CGSizeMake(largeCellSideLength, largeCellSideLength);
        CGSize smallCellSize = CGSizeMake(smallCellSideLength, smallCellSideLength);
        if ([self.delegate respondsToSelector:@selector(collectionView:sizeForLargeItemsInSection:)]) {
            if (!CGSizeEqualToSize([self.delegate collectionView:self.collectionView sizeForLargeItemsInSection:i], CGSizeZero)) {
                largeCellSize = [self.delegate collectionView:self.collectionView sizeForLargeItemsInSection:i];
                smallCellSize = CGSizeMake(collectionViewSize.width - largeCellSize.width - itemSpacing - insets.left - insets.right, (largeCellSize.height / 2.f) - (itemSpacing / 2.f));
            }
        }
        lastSmallCellHeight = smallCellSize.height;
        CGFloat largeCellHeight = largeCellSize.height;
        CGFloat lineHeight = numberOfLines * (largeCellHeight + lineSpacing) - lineSpacing;
        contentHeight += lineHeight;
    }
    
    NSInteger numberOfItemsInLastSection = [self.collectionView numberOfItemsInSection:numberOfSections -1];
    if ((numberOfItemsInLastSection - 1) % 3 == 0 && (numberOfItemsInLastSection - 1) % 6 != 0) {
        contentHeight -= lastSmallCellHeight + itemSpacing;
    }
    return contentHeight;
}

- (void)setDelegate:(id<CollectionViewFlowLayoutDelegate>)delegate{
    self.collectionView.delegate = delegate;
}

- (id<CollectionViewFlowLayoutDelegate>)delegate{
    return (id<CollectionViewFlowLayoutDelegate>)self.collectionView.delegate;
}

- (CGSize)collectionViewContentSize{
    CGSize contentSize = CGSizeMake(_collectionViewSize.width, 0);
    for (NSInteger i = 0; i < self.collectionView.numberOfSections; i++) {
        if ([self.collectionView numberOfItemsInSection:i] == 0) {
            break;
        }
        NSInteger numberOfLines = ceil((CGFloat)[self.collectionView numberOfItemsInSection:i] / 3.f);
        CGFloat lineHeight = numberOfLines * ([_largeCellSizeArray[i] CGSizeValue].height + _lineSpacing) - _lineSpacing;
        contentSize.height += lineHeight;
    }
    contentSize.height += _insets.top + _insets.bottom + _sectionSpacing * (self.collectionView.numberOfSections - 1);
    NSInteger numberOfItemsInLastSection = [self.collectionView numberOfItemsInSection:self.collectionView.numberOfSections - 1];
    if ((numberOfItemsInLastSection - 1) % 3 == 0 && (numberOfItemsInLastSection - 1) % 6 != 0) {
        contentSize.height -= [_smallCellSizeArray[self.collectionView.numberOfSections - 1] CGSizeValue].height + _itemSpacing;
    }
    return contentSize;
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    _oldRect = rect;
    NSMutableArray *attributesArray = [NSMutableArray array];
    for (NSInteger i = 0; i < self.collectionView.numberOfSections; i++) {
        NSInteger numberOfCellsInSection = [self.collectionView numberOfItemsInSection:i];
        for (NSInteger j = 0; j < numberOfCellsInSection; j++) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:j inSection:i];
            UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
            if (CGRectIntersectsRect(rect, attributes.frame)) {
                [attributesArray addObject:attributes];
            }
        }
    }
    _oldArray = attributesArray;
    return  attributesArray;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes *attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    
    //cellSize
    CGFloat largeCellSideLength = (2.f * (_collectionViewSize.width - _insets.left - _insets.right) - _itemSpacing) / 3.f;
    CGFloat smallCellSideLength = (largeCellSideLength - _itemSpacing) / 2.f;
    _largeCellSize = CGSizeMake(largeCellSideLength, largeCellSideLength);
    _smallCellSize = CGSizeMake(smallCellSideLength, smallCellSideLength);
    if ([self.delegate respondsToSelector:@selector(collectionView:sizeForLargeItemsInSection:)]) {
        if (!CGSizeEqualToSize([self.delegate collectionView:self.collectionView sizeForLargeItemsInSection:indexPath.section], CGSizeZero)) {
            _largeCellSize = [self.delegate collectionView:self.collectionView sizeForLargeItemsInSection:indexPath.section];
            _smallCellSize = CGSizeMake(_collectionViewSize.width - _largeCellSize.width - _itemSpacing - _insets.left - _insets.right, (_largeCellSize.height / 2.f) - (_itemSpacing / 2.f));
        }
    }
    if (!_largeCellSizeArray) {
        _largeCellSizeArray = [NSMutableArray array];
    }
    if (!_smallCellSizeArray) {
        _smallCellSizeArray = [NSMutableArray array];
    }
    _largeCellSizeArray[indexPath.section] = [NSValue valueWithCGSize:_largeCellSize];
    _smallCellSizeArray[indexPath.section] = [NSValue valueWithCGSize:_smallCellSize];
    
    //section height
    CGFloat sectionHeight = 0;
    for (NSInteger i = 0; i <= indexPath.section - 1; i++) {
        NSInteger cellsCount = [self.collectionView numberOfItemsInSection:i];
        CGFloat largeCellHeight = [_largeCellSizeArray[i] CGSizeValue].height;
        CGFloat smallCellHeight = [_smallCellSizeArray[i] CGSizeValue].height;
        NSInteger lines = ceil((CGFloat)cellsCount / 3.f);
        sectionHeight += lines * (_lineSpacing + largeCellHeight) + _sectionSpacing;
        if ((cellsCount - 1) % 3 == 0 && (cellsCount - 1) % 6 != 0) {
            sectionHeight -= smallCellHeight + _itemSpacing;
        }
    }
    if (sectionHeight > 0) {
        sectionHeight -= _lineSpacing;
    }
    
    NSInteger line = indexPath.item / 3;
    CGFloat lineSpaceForIndexPath = _lineSpacing * line;
    CGFloat lineOriginY = _largeCellSize.height * line + sectionHeight + lineSpaceForIndexPath + _insets.top;
    CGFloat rightSideLargeCellOriginX = _collectionViewSize.width - _largeCellSize.width - _insets.right;
    CGFloat rightSideSmallCellOriginX = _collectionViewSize.width - _smallCellSize.width - _insets.right;
    
    if (indexPath.item % 6 == 0) {
        attribute.frame = CGRectMake(_insets.left, lineOriginY, _largeCellSize.width, _largeCellSize.height);
    }else if ((indexPath.item + 1) % 6 == 0) {
        attribute.frame = CGRectMake(rightSideLargeCellOriginX, lineOriginY, _largeCellSize.width, _largeCellSize.height);
    }else if (line % 2 == 0) {
        if (indexPath.item % 2 != 0) {
            attribute.frame = CGRectMake(rightSideSmallCellOriginX, lineOriginY, _smallCellSize.width, _smallCellSize.height);
        }else {
            attribute.frame =CGRectMake(rightSideSmallCellOriginX, lineOriginY + _smallCellSize.height + _itemSpacing, _smallCellSize.width, _smallCellSize.height);
        }
    }else {
        if (indexPath.item % 2 != 0) {
            attribute.frame = CGRectMake(_insets.left, lineOriginY, _smallCellSize.width, _smallCellSize.height);
        }else {
            attribute.frame =CGRectMake(_insets.left, lineOriginY + _smallCellSize.height + _itemSpacing, _smallCellSize.width, _smallCellSize.height);
        }
    }
    return attribute;
}

@end
View Code

FlowLayout_6

#import "FlowLayout6.h"

@protocol CollectionViewResetFlowLayoutDelegate <CollectionViewFlowLayoutDelegate>
@optional
- (CGFloat)reorderingItemAlpha:(UICollectionView * )collectionview; //Default 0.
- (UIEdgeInsets)autoScrollTrigerEdgeInsets:(UICollectionView *)collectionView; //not supported horizontal scroll.
- (UIEdgeInsets)autoScrollTrigerPadding:(UICollectionView *)collectionView;
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath;
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didBeginDraggingItemAtIndexPath:(NSIndexPath *)indexPath;
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout willEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath;
- (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout didEndDraggingItemAtIndexPath:(NSIndexPath *)indexPath;
@end


@protocol CollectionViewResetFlowLayoutDataSource <CollectionViewFlowLayoutDatasource>
@optional
- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath willMoveToIndexPath:(NSIndexPath *)toIndexPath;
- (void)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath didMoveToIndexPath:(NSIndexPath *)toIndexPath;
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath;
- (BOOL)collectionView:(UICollectionView *)collectionView itemAtIndexPath:(NSIndexPath *)fromIndexPath canMoveToIndexPath:(NSIndexPath *)toIndexPath;
@end


@interface FlowLayout_6 : FlowLayout6 <UIGestureRecognizerDelegate>
@property (nonatomic, strong, readonly) UILongPressGestureRecognizer *longPressGesture;
@property (nonatomic, strong, readonly) UIPanGestureRecognizer * panGesture;
@property (nonatomic, weak) id<CollectionViewResetFlowLayoutDelegate> delegate;
@property (nonatomic, weak) id<CollectionViewResetFlowLayoutDataSource> datasource;
@end
View Code
#import "FlowLayout_6.h"
typedef NS_ENUM(NSInteger, RAScrollDirction) {
    RAScrollDirctionNone,
    RAScrollDirctionUp,
    RAScrollDirctionDown
};

@interface UIImageView (FlowLayout_6)
- (void)setCellCopiedImage:(UICollectionViewCell *)cell;
@end

@implementation UIImageView (FlowLayout_6)
- (void)setCellCopiedImage:(UICollectionViewCell *)cell {
    UIGraphicsBeginImageContextWithOptions(cell.bounds.size, NO, 4.f);
    [cell.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.image = image;
}
@end

@interface FlowLayout_6()
@property (nonatomic, strong) UIView * cellFakeView;
@property (nonatomic, strong) CADisplayLink * displayLink;
@property (nonatomic, assign) RAScrollDirction scrollDirection;
@property (nonatomic, strong) NSIndexPath * reorderingCellIndexPath;
@property (nonatomic, assign) CGPoint reorderingCellCenter;
@property (nonatomic, assign) CGPoint cellFakeViewCenter;
@property (nonatomic, assign) CGPoint panTranslation;
@property (nonatomic, assign) UIEdgeInsets scrollTrigerEdgeInsets;
@property (nonatomic, assign) UIEdgeInsets scrollTrigePadding;
@property (nonatomic, assign) BOOL setUped;
@end

@implementation FlowLayout_6
- (void)setDelegate:(id<CollectionViewResetFlowLayoutDelegate>)delegate{
    self.collectionView.delegate = delegate;
}

- (id<CollectionViewResetFlowLayoutDelegate>)delegate{
    return (id<CollectionViewResetFlowLayoutDelegate>)self.collectionView.delegate;
}

- (void)setDatasource:(id<CollectionViewResetFlowLayoutDataSource>)datasource{
    self.collectionView.dataSource = datasource;
}

- (id<CollectionViewResetFlowLayoutDataSource>)datasource{
    return (id<CollectionViewResetFlowLayoutDataSource>)self.collectionView.dataSource;
}

- (void)prepareLayout{
    [super prepareLayout];
    //gesture
    [self setUpCollectionViewGesture];
    //scroll triger insets
    _scrollTrigerEdgeInsets = UIEdgeInsetsMake(50.f, 50.f, 50.f, 50.f);
    if ([self.delegate respondsToSelector:@selector(autoScrollTrigerEdgeInsets:)]) {
        _scrollTrigerEdgeInsets = [self.delegate autoScrollTrigerEdgeInsets:self.collectionView];
    }
    //scroll triger padding
    _scrollTrigePadding = UIEdgeInsetsMake(0, 0, 0, 0);
    if ([self.delegate respondsToSelector:@selector(autoScrollTrigerPadding:)]) {
        _scrollTrigePadding = [self.delegate autoScrollTrigerPadding:self.collectionView];
    }
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes *attribute = [super layoutAttributesForItemAtIndexPath:indexPath];
    if (attribute.representedElementCategory == UICollectionElementCategoryCell) {
        if ([attribute.indexPath isEqual:_reorderingCellIndexPath]) {
            CGFloat alpha = 0;
            if ([self.delegate respondsToSelector:@selector(reorderingItemAlpha:)]) {
                alpha = [self.delegate reorderingItemAlpha:self.collectionView];
                if (alpha >= 1.f) {
                    alpha = 1.f;
                }else if (alpha <= 0) {
                    alpha = 0;
                }
            }
            attribute.alpha = alpha;
        }
    }
    return attribute;
}

- (void)setUpCollectionViewGesture{
    if (!_setUped) {
        _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleLongPressGesture:)];
        _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
        _longPressGesture.delegate = self;
        _panGesture.delegate = self;
        for (UIGestureRecognizer * gestureRecognizer in self.collectionView.gestureRecognizers) {
            if ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) {
                [gestureRecognizer requireGestureRecognizerToFail:_longPressGesture]; }}
        [self.collectionView addGestureRecognizer:_longPressGesture];
        [self.collectionView addGestureRecognizer:_panGesture];
        _setUped = YES;
    }
}

- (void)handleLongPressGesture:(UILongPressGestureRecognizer *)longPress
{
    switch (longPress.state) {
        case UIGestureRecognizerStateBegan: {
            //indexPath
            NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:[longPress locationInView:self.collectionView]];
            //can move
            if ([self.datasource respondsToSelector:@selector(collectionView:canMoveItemAtIndexPath:)]) {
                if (![self.datasource collectionView:self.collectionView canMoveItemAtIndexPath:indexPath]) {
                    return;
                }
            }
            //will begin dragging
            if ([self.delegate respondsToSelector:@selector(collectionView:layout:willBeginDraggingItemAtIndexPath:)]) {
                [self.delegate collectionView:self.collectionView layout:self willBeginDraggingItemAtIndexPath:indexPath];
            }
            
            //indexPath
            _reorderingCellIndexPath = indexPath;
            //scrolls top off
            self.collectionView.scrollsToTop = NO;
            //cell fake view
            UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:indexPath];
            _cellFakeView = [[UIView alloc] initWithFrame:cell.frame];
            _cellFakeView.layer.shadowColor = [UIColor blackColor].CGColor;
            _cellFakeView.layer.shadowOffset = CGSizeMake(0, 0);
            _cellFakeView.layer.shadowOpacity = .5f;
            _cellFakeView.layer.shadowRadius = 3.f;
            UIImageView *cellFakeImageView = [[UIImageView alloc] initWithFrame:cell.bounds];
            UIImageView *highlightedImageView = [[UIImageView alloc] initWithFrame:cell.bounds];
            cellFakeImageView.contentMode = UIViewContentModeScaleAspectFill;
            highlightedImageView.contentMode = UIViewContentModeScaleAspectFill;
            cellFakeImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
            highlightedImageView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
            cell.highlighted = YES;
            [highlightedImageView setCellCopiedImage:cell];
            cell.highlighted = NO;
            [cellFakeImageView setCellCopiedImage:cell];
            [self.collectionView addSubview:_cellFakeView];
            [_cellFakeView addSubview:cellFakeImageView];
            [_cellFakeView addSubview:highlightedImageView];
            //set center
            _reorderingCellCenter = cell.center;
            _cellFakeViewCenter = _cellFakeView.center;
            [self invalidateLayout];
            //animation
            CGRect fakeViewRect = CGRectMake(cell.center.x - (self.smallCellSize.width / 2.f), cell.center.y - (self.smallCellSize.height / 2.f), self.smallCellSize.width, self.smallCellSize.height);
            [UIView animateWithDuration:.3f delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
                _cellFakeView.center = cell.center;
                _cellFakeView.frame = fakeViewRect;
                _cellFakeView.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
                highlightedImageView.alpha = 0;
            } completion:^(BOOL finished) {
                [highlightedImageView removeFromSuperview];
            }];
            //did begin dragging
            if ([self.delegate respondsToSelector:@selector(collectionView:layout:didBeginDraggingItemAtIndexPath:)]) {
                [self.delegate collectionView:self.collectionView layout:self didBeginDraggingItemAtIndexPath:indexPath];
            }
            break;
        }
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled: {
            NSIndexPath *currentCellIndexPath = _reorderingCellIndexPath;
            //will end dragging
            if ([self.delegate respondsToSelector:@selector(collectionView:layout:willEndDraggingItemAtIndexPath:)]) {
                [self.delegate collectionView:self.collectionView layout:self willEndDraggingItemAtIndexPath:currentCellIndexPath];
            }
            
            //scrolls top on
            self.collectionView.scrollsToTop = YES;
            //disable auto scroll
            [self invalidateDisplayLink];
            //remove fake view
            UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:currentCellIndexPath];
            [UIView animateWithDuration:.3f delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
                _cellFakeView.transform = CGAffineTransformIdentity;
                _cellFakeView.frame = attributes.frame;
            } completion:^(BOOL finished) {
                [_cellFakeView removeFromSuperview];
                _cellFakeView = nil;
                _reorderingCellIndexPath = nil;
                _reorderingCellCenter = CGPointZero;
                _cellFakeViewCenter = CGPointZero;
                [self invalidateLayout];
                if (finished) {
                    //did end dragging
                    if ([self.delegate respondsToSelector:@selector(collectionView:layout:didEndDraggingItemAtIndexPath:)]) {
                        [self.delegate collectionView:self.collectionView layout:self didEndDraggingItemAtIndexPath:currentCellIndexPath];
                    }
                }
            }];
            break;
        }
        default:
            break;
    }
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
    switch (pan.state) {
        case UIGestureRecognizerStateChanged: {
            //translation
            _panTranslation = [pan translationInView:self.collectionView];
            _cellFakeView.center = CGPointMake(_cellFakeViewCenter.x + _panTranslation.x, _cellFakeViewCenter.y + _panTranslation.y);
            //move layout
            [self moveItemIfNeeded];
            //scroll
            if (CGRectGetMaxY(_cellFakeView.frame) >= self.collectionView.contentOffset.y + (self.collectionView.bounds.size.height - _scrollTrigerEdgeInsets.bottom -_scrollTrigePadding.bottom)) {
                if (ceilf(self.collectionView.contentOffset.y) < self.collectionView.contentSize.height - self.collectionView.bounds.size.height) {
                    self.scrollDirection = RAScrollDirctionDown;
                    [self setUpDisplayLink];
                }
            }else if (CGRectGetMinY(_cellFakeView.frame) <= self.collectionView.contentOffset.y + _scrollTrigerEdgeInsets.top + _scrollTrigePadding.top) {
                if (self.collectionView.contentOffset.y > -self.collectionView.contentInset.top) {
                    self.scrollDirection = RAScrollDirctionUp;
                    [self setUpDisplayLink];
                }
            }else {
                self.scrollDirection = RAScrollDirctionNone;
                [self invalidateDisplayLink];
            }
            break;
        }
        case UIGestureRecognizerStateCancelled:
        case UIGestureRecognizerStateEnded:
            [self invalidateDisplayLink];
            break;
            
        default:
            break;
    }
}

- (void)setUpDisplayLink
{
    if (_displayLink) {
        return;
    }
    _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(autoScroll)];
    [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}

-  (void)invalidateDisplayLink
{
    [_displayLink invalidate];
    _displayLink = nil;
}

- (void)autoScroll
{
    CGPoint contentOffset = self.collectionView.contentOffset;
    UIEdgeInsets contentInset = self.collectionView.contentInset;
    CGSize contentSize = self.collectionView.contentSize;
    CGSize boundsSize = self.collectionView.bounds.size;
    CGFloat increment = 0;
    
    if (self.scrollDirection == RAScrollDirctionDown) {
        CGFloat percentage = (((CGRectGetMaxY(_cellFakeView.frame) - contentOffset.y) - (boundsSize.height - _scrollTrigerEdgeInsets.bottom - _scrollTrigePadding.bottom)) / _scrollTrigerEdgeInsets.bottom);
        increment = 10 * percentage;
        if (increment >= 10.f) {
            increment = 10.f;
        }
    }else if (self.scrollDirection == RAScrollDirctionUp) {
        CGFloat percentage = (1.f - ((CGRectGetMinY(_cellFakeView.frame) - contentOffset.y - _scrollTrigePadding.top) / _scrollTrigerEdgeInsets.top));
        increment = -10.f * percentage;
        if (increment <= -10.f) {
            increment = -10.f;
        }
    }
    
    if (contentOffset.y + increment <= -contentInset.top) {
        [UIView animateWithDuration:.07f delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            CGFloat diff = -contentInset.top - contentOffset.y;
            self.collectionView.contentOffset = CGPointMake(contentOffset.x, -contentInset.top);
            _cellFakeViewCenter = CGPointMake(_cellFakeViewCenter.x, _cellFakeViewCenter.y + diff);
            _cellFakeView.center = CGPointMake(_cellFakeViewCenter.x + _panTranslation.x, _cellFakeViewCenter.y + _panTranslation.y);
        } completion:nil];
        [self invalidateDisplayLink];
        return;
    }else if (contentOffset.y + increment >= contentSize.height - boundsSize.height - contentInset.bottom) {
        [UIView animateWithDuration:.07f delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
            CGFloat diff = contentSize.height - boundsSize.height - contentInset.bottom - contentOffset.y;
            self.collectionView.contentOffset = CGPointMake(contentOffset.x, contentSize.height - boundsSize.height - contentInset.bottom);
            _cellFakeViewCenter = CGPointMake(_cellFakeViewCenter.x, _cellFakeViewCenter.y + diff);
            _cellFakeView.center = CGPointMake(_cellFakeViewCenter.x + _panTranslation.x, _cellFakeViewCenter.y + _panTranslation.y);
        } completion:nil];
        [self invalidateDisplayLink];
        return;
    }
    
    [self.collectionView performBatchUpdates:^{
        _cellFakeViewCenter = CGPointMake(_cellFakeViewCenter.x, _cellFakeViewCenter.y + increment);
        _cellFakeView.center = CGPointMake(_cellFakeViewCenter.x + _panTranslation.x, _cellFakeViewCenter.y + _panTranslation.y);
        self.collectionView.contentOffset = CGPointMake(contentOffset.x, contentOffset.y + increment);
    } completion:nil];
    [self moveItemIfNeeded];
}

- (void)moveItemIfNeeded
{
    NSIndexPath *atIndexPath = _reorderingCellIndexPath;
    NSIndexPath *toIndexPath = [self.collectionView indexPathForItemAtPoint:_cellFakeView.center];
    
    if (toIndexPath == nil || [atIndexPath isEqual:toIndexPath]) {
        return;
    }
    //can move
    if ([self.datasource respondsToSelector:@selector(collectionView:itemAtIndexPath:canMoveToIndexPath:)]) {
        if (![self.datasource collectionView:self.collectionView itemAtIndexPath:atIndexPath canMoveToIndexPath:toIndexPath]) {
            return;
        }
    }
    
    //will move
    if ([self.datasource respondsToSelector:@selector(collectionView:itemAtIndexPath:willMoveToIndexPath:)]) {
        [self.datasource collectionView:self.collectionView itemAtIndexPath:atIndexPath willMoveToIndexPath:toIndexPath];
    }
    
    //move
    [self.collectionView performBatchUpdates:^{
        //update cell indexPath
        _reorderingCellIndexPath = toIndexPath;
        [self.collectionView moveItemAtIndexPath:atIndexPath toIndexPath:toIndexPath];
        //did move
        if ([self.datasource respondsToSelector:@selector(collectionView:itemAtIndexPath:didMoveToIndexPath:)]) {
            [self.datasource collectionView:self.collectionView itemAtIndexPath:atIndexPath didMoveToIndexPath:toIndexPath];
        }
    } completion:nil];
}

#pragma mark - UIGestureRecognizerDelegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if ([_panGesture isEqual:gestureRecognizer]) {
        if (_longPressGesture.state == 0 || _longPressGesture.state == 5) {
            return NO;
        }
    }else if ([_longPressGesture isEqual:gestureRecognizer]) {
        if (self.collectionView.panGestureRecognizer.state != 0 && self.collectionView.panGestureRecognizer.state != 5) {
            return NO;
        }
    }
    return YES;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([_panGesture isEqual:gestureRecognizer]) {
        if (_longPressGesture.state != 0 && _longPressGesture.state != 5) {
            if ([_longPressGesture isEqual:otherGestureRecognizer]) {
                return YES;
            }
            return NO;
        }
    }else if ([_longPressGesture isEqual:gestureRecognizer]) {
        if ([_panGesture isEqual:otherGestureRecognizer]) {
            return YES;
        }
    }else if ([self.collectionView.panGestureRecognizer isEqual:gestureRecognizer]) {
        if (_longPressGesture.state == 0 || _longPressGesture.state == 5) {
            return NO;
        }
    }
    return YES;
}

@end
View Code

7、

UIViewController

#import "ViewController7.h"
#import "PopView.h"
#import "Masonry.h"
#import "Constant.h"
#import "ItemModel.h"

@interface ViewController7 ()<PopViewDelegate>
@property (nonatomic, strong) PopView * popView;
@property (nonatomic, strong) NSMutableArray * dataSource;
@property (nonatomic, strong) UIButton * showBtn;
@property (nonatomic, strong) UIImageView * imageView;
@end

@implementation ViewController7

- (void)viewDidLoad {
    [super viewDidLoad];
    self.showBtn = [UIButton buttonWithType:UIButtonTypeCustom];
    self.showBtn.backgroundColor = [UIColor blueColor];
    [self.showBtn setTitle:@"showPhoto" forState:UIControlStateNormal];
    [self.showBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    self.showBtn.titleLabel.font = [UIFont boldSystemFontOfSize:20.0f];
    [self.showBtn addTarget:self action:@selector(showPhotoBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.showBtn];
    [self.showBtn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.equalTo(self.view).with.offset(100);
        make.size.mas_equalTo(CGSizeMake(200, 80));
    }];
    
    self.imageView = [[UIImageView alloc] init];
    self.imageView.contentMode = UIViewContentModeScaleAspectFill;
    self.imageView.image = [UIImage imageNamed:@"0"];
    self.imageView.backgroundColor = [UIColor redColor];
    [self.view addSubview:self.imageView];
    [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.view);
        make.top.equalTo(self.showBtn.mas_bottom).offset(50);
        make.size.mas_equalTo(CGSizeMake(200, 200));
    }];
}

// 点击加载popView到self.view上面
- (void)showPhotoBtnClicked:(UIButton *)btn{
    [self.popView showInSuperView:self.view];
}

// 弹窗内部collectionView item的点击回调
- (void)closePopView{
    [self.popView removeFromSuperview];
}

- (void)selectedHero:(ItemModel *)item{
    [self closePopView];
    self.imageView.image = [UIImage imageNamed:item.imageName];
}


#pragma mark - 懒加载数据
- (PopView *)popView{
    if (_popView == nil) {
        _popView = [[PopView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
        _popView.dataSource = self.dataSource;
        _popView.delegate = self;
    }
    return _popView;
}

- (NSMutableArray *)dataSource{
    if (_dataSource == nil) {
        _dataSource = [[NSMutableArray alloc] init];
        for (NSInteger i = 0; i < 11; i ++) {
            ItemModel * model = [[ItemModel alloc] init];
            model.imageName = [NSString stringWithFormat:@"%zd",i];
            model.titleName = [NSString stringWithFormat:@"第%zd张",i];
            [_dataSource addObject:model];
        }
    }
    return _dataSource;
}
View Code

PopView : UIView

#import <UIKit/UIKit.h>
#import "ItemModel.h"

@protocol PopViewDelegate <NSObject>
- (void)selectedHero:(ItemModel *)item;
- (void)closePopView;
@end

@interface PopView : UIView
@property (nonatomic,weak) id<PopViewDelegate>delegate;
@property (nonatomic,strong) NSArray * dataSource;
- (void)showInSuperView:(UIView *)superView;
@end
View Code
#import "PopView.h"
#import "Masonry.h"
#import "UIView+Extension.h"
#import "Constant.h"
#import "FlowLayout7.h"
#import "PopCollectionViewCell.h"

@interface PopView()<UICollectionViewDelegate,UICollectionViewDataSource,CollectionViewFlowLayoutDelegate>

@property (nonatomic,strong) UIView * underBackView;
@property (nonatomic,strong) UICollectionView * collectionView;
@property (nonatomic,strong) UILabel * nameLabel;
@property (nonatomic,strong) UIButton * selectedButton;
@property (nonatomic,strong) UIButton * closeButton;
@property (nonatomic,assign) NSInteger selectedIndex;
@end

static NSString * indentify = @"CollectionViewCell";
@implementation PopView
{
    NSInteger _selectedIndex;
}

- (void)showInSuperView:(UIView *)superView{
    CAKeyframeAnimation * popAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    popAnimation.duration = 0.25;
    popAnimation.values = @[
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.1f, 0.1f, 1.0f)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0f, 1.0f, 1.0f)]];
    popAnimation.keyTimes = @[@0.2f, @1.0f];
    popAnimation.timingFunctions = @[
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    [superView addSubview:self];
    [self.underBackView.layer addAnimation:popAnimation forKey:nil];
}

// 初始化 设置背景颜色透明点,然后加载子视图
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self){
        _selectedIndex = 0;
        self.backgroundColor = RGB(51, 51, 51, 0.5);
        [self addsubviews];
    }
    return self;
}

// 加载子视图
- (void)addsubviews{
    [self addSubview:self.underBackView];
    
    [self.underBackView addSubview:self.collectionView];
    [self.underBackView addSubview:self.nameLabel];
    [self.underBackView addSubview:self.selectedButton];
    [self.underBackView addSubview:self.closeButton];
    
    [self.closeButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.right.equalTo(self.underBackView.mas_right).with.offset(-5);
        make.top.equalTo(self.underBackView.mas_top).with.offset(0);
        make.size.mas_equalTo(CGSizeMake(30, 30));
    }];
    
    [self.selectedButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.underBackView);
        make.bottom.equalTo(self.underBackView.mas_bottom).with.offset(-10);
        make.size.mas_equalTo(CGSizeMake(200, 30));
    }];
    
    [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.equalTo(self.selectedButton);
        make.bottom.equalTo(self.selectedButton.mas_top).with.offset(-10);
        make.size.mas_equalTo(CGSizeMake(200, 45));
    }];
}

#pragma makr - collectionView delegate
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.dataSource.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    ItemModel * model = self.dataSource[indexPath.item];
    PopCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:indentify forIndexPath:indexPath];
    cell.heroImageVIew.image = [UIImage imageNamed:model.imageName];
    return cell;
}

// 点击item的时候
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    CGPoint pInUnderView = [self.underBackView convertPoint:collectionView.center toView:collectionView];
    // 获取中间的indexpath
    NSIndexPath *indexpathNew = [collectionView indexPathForItemAtPoint:pInUnderView];
    if (indexPath.row == indexpathNew.row){
        NSLog(@"点击了同一个");
        return;
    }else{
        [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    }
}

#pragma mark - 懒加载
- (UIView *)underBackView{
    if (_underBackView == nil) {
        _underBackView = [[UIView alloc] init];
        _underBackView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.8];
        _underBackView.originX = 30;
        _underBackView.originY = 60;
        _underBackView.width = SCREEN_WIDTH - 2 * _underBackView.originX;
        _underBackView.height = SCREEN_HEIGHT - 2 * _underBackView.originY;
        _underBackView.layer.cornerRadius = 5;
        _underBackView.layer.borderColor = [UIColor redColor].CGColor;
        _underBackView.layer.borderWidth = 2.0f;
    }
    return _underBackView;
}

- (UILabel *)nameLabel{
    if (_nameLabel == nil) {
        _nameLabel = [[UILabel alloc] init];
        _nameLabel.textAlignment = NSTextAlignmentCenter;
        _nameLabel.backgroundColor = [UIColor whiteColor];
        _nameLabel.font = [UIFont boldSystemFontOfSize:20];
        _nameLabel.textColor = [UIColor blueColor];
        _nameLabel.layer.cornerRadius = 5.0f;
        _nameLabel.layer.borderColor = [UIColor blackColor].CGColor;
        _nameLabel.layer.borderWidth = 2.0f;
    }
    return _nameLabel;
}

- (UIButton *)selectedButton{
    if (_selectedButton == nil) {
        _selectedButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _selectedButton.backgroundColor = [UIColor blackColor];
        [_selectedButton setTitle:@"选这个" forState:UIControlStateNormal];
        [_selectedButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [_selectedButton addTarget:self action:@selector(chooseDone:) forControlEvents:UIControlEventTouchUpInside];
        _selectedButton.layer.cornerRadius = 20.0f;
        _selectedButton.layer.borderWidth = 2.0f;
        _selectedButton.layer.borderColor = [UIColor whiteColor].CGColor;
    }
    return _selectedButton;
}

- (void)chooseDone:(UIButton *)button{
    if (self.delegate && [self.delegate respondsToSelector:@selector(selectedHero:)]) {
        [self.delegate selectedHero:self.dataSource[_selectedIndex]];
    }
}

- (UIButton *)closeButton{
    if (_closeButton == nil) {
        _closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _closeButton.backgroundColor = [UIColor redColor];
        [_closeButton setImage:[UIImage imageNamed:@"close"] forState:UIControlStateNormal];
        [_closeButton addTarget:self action:@selector(close:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _closeButton;
}

- (void)close:(UIButton *)button{
    if (self.delegate && [self.delegate respondsToSelector:@selector(closePopView)]) {
        [self.delegate closePopView];
    }
}

- (UICollectionView *)collectionView{
    if (_collectionView == nil) {
       FlowLayout7 * layout = [[FlowLayout7 alloc] init];
        layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        layout.itemSize = CGSizeMake(self.underBackView.width / 2, self.underBackView.width - 100);
        layout.minimumLineSpacing = 30;
        layout.minimumInteritemSpacing = 30;
        layout.needAlpha = YES;
        layout.delegate = self;
        CGFloat oneX =self.underBackView.width / 4;
        layout.sectionInset = UIEdgeInsetsMake(0, oneX, 0, oneX);
        
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 30, self.underBackView.bounds.size.width, self.underBackView.bounds.size.height * 0.65) collectionViewLayout:layout];
        _collectionView.backgroundColor = [UIColor whiteColor];
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
        _collectionView.showsHorizontalScrollIndicator = NO;
        [_collectionView registerNib:[UINib nibWithNibName:@"PopCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:indentify];
    }
    return _collectionView;
}

#pragma CustomLayout的代理方法
- (void)collectioViewScrollToIndex:(NSInteger)index{
    [self labelText:index];
    _selectedIndex = index;
}

// 第一次加载的时候刷新collectionView
- (void)setDataSource:(NSArray *)dataSource{
    _dataSource = dataSource;
    [self labelText:0];
    [self.collectionView reloadData];
}

// 给指定的label赋值
- (void)labelText:(NSInteger)idx{
    ItemModel * model = self.dataSource[idx];
    self.nameLabel.text = model.titleName;
}
View Code

FlowLayout7 : UICollectionViewFlowLayout

#import <UIKit/UIKit.h>

@protocol CollectionViewFlowLayoutDelegate <NSObject>
- (void)collectioViewScrollToIndex:(NSInteger)index;
@end

@interface FlowLayout7 : UICollectionViewFlowLayout
@property (nonatomic,assign) id<CollectionViewFlowLayoutDelegate>delegate;
@property (nonatomic,assign) BOOL needAlpha;
@end
View Code
#import "FlowLayout7.h"
#import "Constant.h"

@implementation FlowLayout7
{
    NSInteger _index;
}

// 初始化方法
- (instancetype)init{
    if (self == [super init]) {
        _index = 0;
    }
    return self;
}

// 该方法会自动重载
- (void)prepareLayout{
    [super prepareLayout];
}

#pragma mark - 以下三个方法必须一起重载,分别是返回可见区域尺寸、获取可见区域内可见的item数组、当滚动的时候一直重绘collectionView
- (CGSize)collectionViewContentSize{
    return [super collectionViewContentSize];
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    //1. 获取可见区域
    CGRect visibleRect = CGRectMake(self.collectionView.contentOffset.x, 0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
    //2. 获得这个区域的item
    NSArray *visibleItemArray = [super layoutAttributesForElementsInRect:visibleRect];
    
    //3. 遍历,让靠近中心线的item方法,离开的缩小
    for (UICollectionViewLayoutAttributes *attributes in visibleItemArray){
        //1. 获取每个item距离可见区域左侧边框的距离 有正负
        CGFloat leftMargin = attributes.center.x - self.collectionView.contentOffset.x;
        //2. 获取边框距离屏幕中心的距离(固定的)
        CGFloat halfCenterX = self.collectionView.frame.size.width / 2;
        //3. 获取距离中心的的偏移量,需要绝对值
        CGFloat absOffset = fabs(halfCenterX - leftMargin);
        //4. 获取的实际的缩放比例 距离中心越多,这个值就越小,也就是item的scale越小 中心是方法最大的
        CGFloat scale = 1 - absOffset / halfCenterX;
        //5. 缩放
        attributes.transform3D = CATransform3DMakeScale(1 + scale * MKJMinZoomScale, 1 + scale * MKJMinZoomScale, 1);
        // 是否需要透明
        if (self.needAlpha){
            if (scale < 0.6){
                attributes.alpha = 0.6;
            }else if (scale > 0.99){
                attributes.alpha = 1.0;
            }else{
                attributes.alpha = scale;
            }
        }
    }
    NSArray * attributesArr = [[NSArray alloc] initWithArray:visibleItemArray copyItems:YES];
    return attributesArr;
}

// 滚动的时候会一直调用
// 当边界发生变化的时候,是否应该刷新布局。如果YES那么就是边界发生变化的时候,重新计算布局信息  这里的newBounds变化的只有x值的变化,也就是偏移量的变化
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
    // 把collectionView本身的中心位子(固定的),转换成collectionView整个内容上的point
    CGPoint pInView = [self.collectionView.superview convertPoint:self.collectionView.center toView:self.collectionView];
    // 通过坐标获取对应的indexpath
    NSIndexPath *indexPathNow = [self.collectionView indexPathForItemAtPoint:pInView];
    if (indexPathNow.row == 0){
        if (newBounds.origin.x < SCREEN_WIDTH / 2){
            if (_index != indexPathNow.row){
                _index = 0;
                if (self.delegate && [self.delegate respondsToSelector:@selector(collectioViewScrollToIndex:)]){
                    [self.delegate collectioViewScrollToIndex:_index];
                }
            }
        }
    }else{
        if (_index != indexPathNow.row){
            _index = indexPathNow.row;
            if (self.delegate && [self.delegate respondsToSelector:@selector(collectioViewScrollToIndex:)]){
                [self.delegate collectioViewScrollToIndex:_index];
            }
        }
    }
    [super shouldInvalidateLayoutForBoundsChange:newBounds];
    return YES;
}


// 重载第四个属性,item自动中心对齐
// 该方法可写可不写,主要是让滚动的item根据距离中心的值,确定哪个必须展示在中心,不会像普通的那样滚动到哪里就停到哪里
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
    // ProposeContentOffset是本来应该停下的位子
    // 1. 先给一个字段存储最小的偏移量 那么默认就是无限大
    CGFloat minOffset = CGFLOAT_MAX;
    // 2. 获取到可见区域的centerX
    CGFloat horizontalCenter = proposedContentOffset.x + self.collectionView.bounds.size.width / 2;
    // 3. 拿到可见区域的rect
    CGRect visibleRec = CGRectMake(proposedContentOffset.x, 0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
    // 4. 获取到所有可见区域内的item数组
    NSArray *visibleAttributes = [super layoutAttributesForElementsInRect:visibleRec];
    
    // 遍历数组,找到距离中心最近偏移量是多少
    for (UICollectionViewLayoutAttributes *atts in visibleAttributes)
    {
        // 可见区域内每个item对应的中心X坐标
        CGFloat itemCenterX = atts.center.x;
        // 比较是否有更小的,有的话赋值给minOffset
        if (fabs(itemCenterX - horizontalCenter) <= fabs(minOffset)) {
            minOffset = itemCenterX - horizontalCenter;
        }
        
    }
    // 这里需要注意的是  上面获取到的minOffset有可能是负数,那么代表左边的item还没到中心,如果确定这种情况下左边的item是距离最近的,那么需要左边的item居中,意思就是collectionView的偏移量需要比原本更小才是,例如原先是1000的偏移,但是需要展示前一个item,所以需要1000减去某个偏移量,因此不需要更改偏移的正负
    
    // 但是当propose小于0的时候或者大于contentSize(除掉左侧和右侧偏移以及单个cell宽度)  、
    // 防止当第一个或者最后一个的时候不会有居中(偏移量超过了本身的宽度),直接卡在推荐的停留位置
    CGFloat centerOffsetX = proposedContentOffset.x + minOffset;
    if (centerOffsetX < 0) {
        centerOffsetX = 0;
    }
    
    if (centerOffsetX > self.collectionView.contentSize.width -(self.sectionInset.left + self.sectionInset.right + self.itemSize.width)) {
        centerOffsetX = floor(centerOffsetX);
    }
    return CGPointMake(centerOffsetX, proposedContentOffset.y);
}
@end
View Code

PopCollectionViewCell : UICollectionViewCell

#import <UIKit/UIKit.h>

@interface PopCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UIImageView *heroImageVIew;

@end
View Code
#import "PopCollectionViewCell.h"

@implementation PopCollectionViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

@end
View Code

ItemModel : NSObject

#import <Foundation/Foundation.h>

@interface ItemModel : NSObject
@property (nonatomic,copy) NSString *imageName;
@property (nonatomic,copy) NSString *titleName;
@end
View Code

Constant.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#define RGB(r,g,b,a)    [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:a]

#define SCREEN_WIDTH [UIScreen  mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height

UIKIT_EXTERN const CGFloat MKJLineSpacing; // item间距
UIKIT_EXTERN const CGFloat MKJZoomScale; // 缩放比例
UIKIT_EXTERN const CGFloat MKJMinZoomScale; // 最小缩放比例
View Code
#import "Constant.h"

const CGFloat MKJLineSpacing = 40.f;
const CGFloat MKJZoomScale = 1.45f;
const CGFloat MKJMinZoomScale = MKJZoomScale - 1.0f;
View Code

UIView (Extension)

#import <UIKit/UIKit.h>

@interface UIView (Extension)


@property (nonatomic, assign) CGFloat originX;

@property (nonatomic, assign) CGFloat originY;

@property (nonatomic, assign) CGFloat endX;

@property (nonatomic, assign) CGFloat endY;


@property (nonatomic, assign) CGFloat width;

@property (nonatomic, assign) CGFloat height;

@property (nonatomic, assign, readonly) CGPoint centerOfCurrentView;

@property (nonatomic, assign) CGFloat centerXOfCurrentView;

@property (nonatomic, assign) CGFloat centerYOfCurrentView;


@property (nonatomic, assign) CGFloat centerX;

@property (nonatomic, assign) CGFloat centerY;


// 设置圆角
- (void)cornerRadius:(CGFloat)cornerRadius
         borderColor:(CGColorRef)borderColor
         borderWidth:(CGFloat)borderWidth;


@end
View Code
#import "UIView+Extension.h"

@implementation UIView (Extension)
- (CGFloat)originX
{
    return self.frame.origin.x;
}
- (CGFloat)originY
{
    return self.frame.origin.y;
}
- (CGFloat)endX
{
    return self.originX + self.width;
}
- (CGFloat)endY
{
    return self.originY + self.height;
}
- (CGFloat)width
{
    return self.bounds.size.width;
}
- (CGFloat)height
{
    return self.bounds.size.height;
}
- (CGPoint)centerOfCurrentView
{
    return CGPointMake(self.bounds.size.width/2, self.bounds.size.height/2);
}
- (CGFloat)centerXOfCurrentView
{
    return self.bounds.size.width/2;
}
- (CGFloat)centerYOfCurrentView
{
    return self.bounds.size.height/2;
}

- (CGFloat)centerX
{
    return (self.frame.origin.x+self.bounds.size.width/2);
}
- (CGFloat)centerY
{
    return (self.frame.origin.y+self.bounds.size.height/2);
}

- (void)setOriginX:(CGFloat)originX
{
    CGRect frame = self.frame;
    frame.origin.x = originX;
    self.frame = frame;
}
- (void)setOriginY:(CGFloat)originY
{
    CGRect frame = self.frame;
    frame.origin.y = originY;
    self.frame = frame;
}
- (void)setWidth:(CGFloat)width
{
    CGRect frame = self.frame;
    frame.size.width = width;
    self.frame = frame;
}
- (void)setHeight:(CGFloat)height
{
    CGRect frame = self.frame;
    frame.size.height = height;
    self.frame = frame;
}
- (void)setCenterOfCurrentView:(CGPoint)centerOfCurrentView
{
    // readonly
}
- (void)setCenterX:(CGFloat)centerX
{
    CGRect frame = self.frame;
    frame.origin.x = centerX - self.width/2;
    self.frame = frame;
}
- (void)setCenterY:(CGFloat)centerY
{
    CGRect frame = self.frame;
    frame.origin.y = centerY - self.height/2;
    self.frame = frame;
}
- (void)setEndX:(CGFloat)endX
{
    CGRect frame = self.frame;
    frame.origin.x = endX - self.width;
    self.frame = frame;
}
- (void)setEndY:(CGFloat)endY
{
    CGRect frame = self.frame;
    frame.origin.y = endY - self.height;
    self.frame = frame;
}
- (void)cornerRadius:(CGFloat)cornerRadius
         borderColor:(CGColorRef)borderColor
         borderWidth:(CGFloat)borderWidth
{
    self.clipsToBounds      = YES;
    self.layer.cornerRadius = cornerRadius;
    self.layer.borderColor  = borderColor;
    self.layer.borderWidth  = borderWidth;
    
}

@end
View Code

8、

ViewController

#import "ViewController8.h"
#import "CardLayout.h"
#import "CardSelectLayout.h"
#import "CardCollectionViewCell.h"

#define RGBAColor(r,g,b,a)  [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]
#define RGBColor(r,g,b)     RGBAColor(r,g,b,1.0)
#define RGBColorC(c)        RGBColor((((int)c) >> 16),((((int)c) >> 8) & 0xff),(((int)c) & 0xff))
static CGFloat collectionHeight;

@interface ViewController8 ()<UICollectionViewDelegate, UICollectionViewDataSource,CardLayoutDelegate>
@property (nonatomic, strong) UICollectionView * collectionView;
@property (nonatomic, strong) UITapGestureRecognizer * tapGesture;
@property (nonatomic, strong) CardLayout * cardLayout;
@property (nonatomic, strong) CardSelectLayout * cardSelectLayout;
@property (nonatomic, strong) UICollectionViewLayout * layout;
@end

@implementation ViewController8

- (void)viewDidLoad {
    [super viewDidLoad];
    collectionHeight = self.view.bounds.size.height;
    self.cardLayout = [[CardLayout alloc] initWithOffsetY:400];
    self.layout = self.cardLayout;
    self.cardLayout.delegate = self;
    [self.view addSubview:self.collectionView];
}

- (UICollectionView *)collectionView {
    if (!_collectionView) {
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, collectionHeight) collectionViewLayout:self.layout];
        [_collectionView registerClass:[CardCollectionViewCell class] forCellWithReuseIdentifier:@"cardCell"];
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
        [_collectionView setContentOffset:CGPointMake(0, 400)];
        _collectionView.backgroundColor = RGBColorC(0x2D3142);
    }
    return _collectionView;
}

- (UITapGestureRecognizer *)tapGesCollectionView {
    if (!_tapGesture) {
        _tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapOnBackGround)];
    }
    return _tapGesture;
}

- (void)tapOnBackGround {
    CGFloat offsetY = self.collectionView.contentOffset.y;
    if ([self.layout isKindOfClass:[CardLayout class]]) {
        
    }else{
        if (!self.cardLayout) {
            self.cardLayout =  [[CardLayout alloc] initWithOffsetY:offsetY];
            self.layout = self.cardLayout;
            self.cardLayout.delegate = self;
        }else{
            self.cardLayout.offsetY = offsetY;
            self.layout = self.cardLayout;
        }
        self.collectionView.scrollEnabled = YES;
        [self.collectionView removeGestureRecognizer:self.tapGesCollectionView];
    }
    [self.collectionView setCollectionViewLayout:self.layout animated:YES];
    [self updateBlur];
}

- (void)updateBlur{
    if ([self.layout isKindOfClass:[CardLayout class]]) {
        for (NSInteger row = 0; row < [self.collectionView numberOfItemsInSection:0]; row++) {
            CardCollectionViewCell * cell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
            CGFloat blur = ((NSNumber *)[((CardLayout *)self.layout).blurList objectAtIndex:row]).floatValue;
            [cell setBlur:blur];
        }
    }else{
        for (NSInteger row = 0; row < [self.collectionView numberOfItemsInSection:0]; row++) {
            CardCollectionViewCell * cell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
            [cell setBlur:0];
        }
    }
}

- (void)updateBlur:(CGFloat) blur ForRow:(NSInteger)row{
    if (![self.layout isKindOfClass:[CardLayout class]]) {
        return;
    }
    CardCollectionViewCell * cell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
    [cell setBlur:blur];
}


#pragma mark - UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 39;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    CardCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cardCell" forIndexPath:indexPath];
    cell.bgColor = [self getGameColor:indexPath.row];
    cell.title = [NSString stringWithFormat:@"Item %d",(int)indexPath.row];
    return cell;
}

- (UIColor *)getGameColor:(NSInteger)index{
    NSArray * colorList = @[RGBColorC(0xfb742a),RGBColorC(0xfcc42d),RGBColorC(0x29c26d),RGBColorC(0xfaa20a),RGBColorC(0x5e64d9),RGBColorC(0x6d7482),RGBColorC(0x54b1ff),RGBColorC(0xe2c179),RGBColorC(0x9973e5),RGBColorC(0x61d4ff)];
    UIColor * color = [colorList objectAtIndex:(index%10)];
    return color;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    CGFloat offsetY = self.collectionView.contentOffset.y;
    if ([self.layout isKindOfClass:[CardLayout class]]) {
        if (!self.cardSelectLayout) {
            self.cardSelectLayout = [[CardSelectLayout alloc] initWithIndexPath:indexPath offsetY:offsetY ContentSizeHeight:((CardLayout *)self.layout).contentSizeHeight];
            self.layout = self.cardSelectLayout;
        }else{
            self.cardSelectLayout.contentOffsetY = offsetY;
            self.cardSelectLayout.contentSizeHeight = ((CardLayout *)self.layout).contentSizeHeight;
            self.cardSelectLayout.selectedIndexPath = indexPath;
            self.layout = self.cardSelectLayout;
        }
        self.collectionView.scrollEnabled = NO;
        [self showMaskView];//显示背景浮层
        //选中的卡片不显示蒙层
        [(CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath] setBlur:0];
    }else{
        if (!self.cardLayout) {
            self.cardLayout = [[CardLayout alloc] initWithOffsetY:offsetY];
            self.layout = self.cardLayout;
            self.cardLayout.delegate = self;
        }else{
            self.cardLayout.offsetY = offsetY;
            self.layout = self.cardLayout;
            self.cardLayout.delegate = self;
        }
        self.collectionView.scrollEnabled = YES;
        [self hideMaskView];
    }
    [self.collectionView setCollectionViewLayout:self.layout animated:YES];
}

- (void)showMaskView{
    //    CATransform3DMakeTranslation(0, 0, -10);
    //    self.maskView.hidden = NO;
    self.collectionView.backgroundColor = RGBColorC(0x161821);
    //    self.closeIconView.hidden = NO;
    [self.collectionView addGestureRecognizer:self.tapGesCollectionView];
}
- (void)hideMaskView{
    //    self.maskView.hidden = YES;
    self.collectionView.backgroundColor = RGBColorC(0x2D3142);
    //    self.closeIconView.hidden = YES;
    [self.collectionView removeGestureRecognizer:self.tapGesCollectionView];
}
View Code

CardLayout : UICollectionViewLayout

#import <UIKit/UIKit.h>

@protocol CardLayoutDelegate <NSObject>
-(void)updateBlur:(CGFloat) blur ForRow:(NSInteger)row;
@end

@interface CardLayout : UICollectionViewLayout

@property(nonatomic, assign) CGFloat offsetY;
@property(nonatomic, assign) CGFloat contentSizeHeight;
@property(nonatomic, strong) NSMutableArray* blurList;
@property(nonatomic, weak) id<CardLayoutDelegate> delegate;
- (instancetype) initWithOffsetY:(CGFloat)offsetY;

@end
View Code
#import "CardLayout.h"
#define GBL_UIKIT_D0 16
#define GBL_UIKIT_D1 12

static CGFloat cellWidth;  //卡片宽度
static CGFloat cellHeight;  //卡片宽度

@interface CardLayout()
@property(nonatomic, assign)CGFloat screenHeight;
@property(nonatomic, assign)CGFloat m0;//是指当第0个cell从初始位置,往上滑m0个点时卡片会移动到最顶点
@property(nonatomic, assign)CGFloat n0;//当contentOffset.y为0时,第0个cell的y坐标为n0
@property(nonatomic, assign)CGFloat deltaOffsetY;//每个cell之间的偏移量间距,即第0个cell往下滑动deltaOffsetY个点时会到达第1个cell的位置
@property(nonatomic, strong)NSMutableArray * cellLayoutList;
@end

@implementation CardLayout

- (NSMutableArray *)blurList{
    if (!_blurList) {
        _blurList = [NSMutableArray array];
        NSInteger rowCount = [self.collectionView numberOfItemsInSection:0];
        for (NSInteger row = 0; row < rowCount; row++) {
            [_blurList addObject:@0];
        }
    }
    return _blurList;
}

- (id)init{
    self = [self initWithOffsetY:0];
    return self;
}

- (instancetype)initWithOffsetY:(CGFloat)offsetY{
    self = [super init];
    if (self) {
        cellWidth = [UIScreen mainScreen].bounds.size.width -12*2;
        cellHeight = 210;
        self.offsetY = offsetY;
        self.cellLayoutList = [NSMutableArray array];
        
        self.screenHeight = [UIScreen mainScreen].bounds.size.height;
        self.m0 = 1000;
        self.n0 = 250;
        self.deltaOffsetY = 140;
    }
    return self;
}

- (void)prepareLayout{
    [super prepareLayout];
    [self.cellLayoutList removeAllObjects];
    NSInteger rowCount = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger row = 0 ; row < rowCount; row++) {
        UICollectionViewLayoutAttributes* attribute = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
        [self.cellLayoutList addObject:attribute];
    }
}

- (CGSize)collectionViewContentSize{
    return CGSizeMake(self.collectionView.frame.size.width,[self getContentSizeY]);
}

- (CGFloat)getContentSizeY{
    self.contentSizeHeight = [self getSizeY];
    return  self.contentSizeHeight;
}

- (CGFloat)getSizeY{
    NSInteger rowCount = [self.collectionView numberOfItemsInSection:0];
    if (rowCount <= 2) {
        return self.collectionView.frame.size.height;
    }
    CGFloat scrollY =  self.deltaOffsetY*(rowCount-1);
    return scrollY + self.screenHeight;
}

//目标offset,在应用layout的时候会调用这个回调来设置collectionView的contentOffset
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset{
    return CGPointMake(0, self.offsetY);
}

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSMutableArray *array = [NSMutableArray array];
    for (UICollectionViewLayoutAttributes* attribute in self.cellLayoutList) {
        if (CGRectIntersectsRect(attribute.frame, rect)) {
            [array addObject:attribute];
        }
    }
    return array;
}

//每次手指滑动时,都会调用这个方法来返回每个cell的布局
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    NSInteger rowCount = [self.collectionView numberOfItemsInSection:0];
    //如果超过两张卡片,则用多卡片布局
    if (rowCount > 2) {
        return [self getAttributesWhen3orMoreRows:indexPath];//超过三张时的布局
    }else{
        return [self getAttributesWhenLessThan2:indexPath];
    }
}


- (UICollectionViewLayoutAttributes *)getAttributesWhen3orMoreRows:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attributes.size = CGSizeMake(cellWidth, cellHeight);
    
    //计算位置
    CGFloat originY = [self getOriginYWithOffsetY:self.collectionView.contentOffset.y row:indexPath.row];
    CGFloat centerY = originY + self.collectionView.contentOffset.y + cellHeight/2.0;
    attributes.center = CGPointMake(CGRectGetWidth(self.collectionView.frame) / 2, centerY);
    
    //计算缩放比例
    CGFloat rat = [self transformRatio:originY];
    attributes.transform = CGAffineTransformMakeScale(rat, rat);
    
    //计算透明度
    //y = (1-1.14x)^0.3
    CGFloat blur = 0;
    if ((1-1.14*rat) < 0 ) {
        blur = 0;
    }else{
        blur = powf((1-1.14*rat), 0.4);
    }
    [self.blurList setObject:@(blur) atIndexedSubscript:indexPath.row];
    if (self.delegate && [self.delegate respondsToSelector:@selector(updateBlur:ForRow:)]) {
        [self.delegate updateBlur:blur ForRow:indexPath.row];
    }
    
    attributes.zIndex = originY;    //这里设置zIndex,是为了cell的层次顺序达到下面的cell覆盖上面的cell的效果
    return attributes;
}

- (UICollectionViewLayoutAttributes*)getAttributesWhenLessThan2:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    CGFloat originY = GBL_UIKIT_D1 + indexPath.row *(cellHeight+GBL_UIKIT_D0);
    attributes.frame = CGRectMake(GBL_UIKIT_D0, originY, cellWidth, cellHeight);
    return attributes;
}

//根据下标、当前偏移量来获取对应的y坐标
- (CGFloat)getOriginYWithOffsetY:(CGFloat)offsetY row:(NSInteger)row{
    // 公式: y0 = ((m0 - x)/m0)^4*n0
    // 公式:  yi=((m0 + i*140-x)/(m0 + i*140))^4*((m0+140*i)/m0)^4*n0
    CGFloat x = offsetY;    //这里offsetY就是自变量x
    CGFloat ni = [self defaultYWithRow:row];
    CGFloat mi = self.m0+row*self.deltaOffsetY;
    CGFloat tmp = mi - x;
    CGFloat y = 0;
    if (tmp >= 0) {
        y = powf((tmp)/mi, 4)*ni;
    }else{
        y = 0 - (cellHeight - tmp);
    }
    return y;
}

//根据偏移量、下标获取对应的尺寸变化
- (CGFloat)transformRatio:(CGFloat)originY{
    // y = (x/range)^0.4
    if (originY < 0) {
        return 1;
    }
    CGFloat range = [UIScreen mainScreen].bounds.size.height ;
    originY = fminf(originY, range);
    CGFloat ratio = powf(originY/range, 0.04);
    return ratio;
}

//获取当contentOffset.y=0时每个cell的y值
- (CGFloat)defaultYWithRow:(NSInteger)row {
    CGFloat x0 = 0;     //初始状态
    CGFloat xi = x0 - self.deltaOffsetY*row;
    CGFloat ni = powf((self.m0 - xi)/self.m0, 4)*self.n0;
    //    NSLog(@"defaultY-%d: %f",(int)row,ni);
    return ni;
}



- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return !CGRectEqualToRect(newBounds, self.collectionView.bounds);
}

@end
View Code

CardSelectLayout : UICollectionViewLayout

#import <UIKit/UIKit.h>

@interface CardSelectLayout : UICollectionViewLayout
@property(nonatomic, assign) NSIndexPath * selectedIndexPath;
@property(nonatomic, assign) CGFloat contentOffsetY;
@property(nonatomic, assign) CGFloat contentSizeHeight;
- (instancetype)initWithIndexPath:(NSIndexPath *)indexPath offsetY:(CGFloat)offsetY ContentSizeHeight:(CGFloat)sizeHeight;
@end
View Code
#import "CardSelectLayout.h"
#import "CardCollectionViewCell.h"

static CGFloat cellWidth;  //卡片宽度
static CGFloat cellHeight;  //卡片宽度

@interface CardSelectLayout()
@property(nonatomic, assign) CGFloat cellToTop;  //距顶部距离
@property(nonatomic, assign) CGFloat cellToBottom;  //距底部距离
@property(nonatomic, strong) NSMutableArray * cellLayoutList;
@end

@implementation CardSelectLayout

- (instancetype)initWithIndexPath:(NSIndexPath *)indexPath offsetY:(CGFloat)offsetY ContentSizeHeight:(CGFloat)sizeHeight{
    self = [self init];
    if (self) {
        self.selectedIndexPath = indexPath;
        self.contentOffsetY = offsetY;
        self.contentSizeHeight = sizeHeight;
    }
    return self;
}

- (instancetype)init{
    self = [super init];
    if (self) {
        cellWidth = [UIScreen mainScreen].bounds.size.width-12*2;
        cellHeight = 210;
        self.cellToTop = -cellHeight;
        self.cellToBottom = [UIScreen mainScreen].bounds.size.height + cellHeight;
        self.cellLayoutList = [NSMutableArray array];
    }
    return self;
}

- (void)prepareLayout {
    [super prepareLayout];
    [self.cellLayoutList removeAllObjects];
    [self.collectionView setContentOffset:CGPointMake(0, self.contentOffsetY)];
    CGFloat scale = 1;
    CGFloat width = cellWidth;
    CGFloat height = cellHeight;
    NSInteger rowsCount = [self.collectionView numberOfItemsInSection:0];
    for (NSInteger row = 0; row < rowsCount; row++) {
        NSIndexPath* cellIndexPath = [NSIndexPath indexPathForRow:row inSection:0];
        UICollectionViewLayoutAttributes* attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:cellIndexPath];
        CGFloat centerX = self.collectionView.bounds.size.width/2.0;
        CGFloat currentY = 0;
        if (row < self.selectedIndexPath.row) {
            currentY = self.contentOffsetY + self.cellToTop ;
            scale = 0.8;
            height = cellHeight;
        }else if (row == self.selectedIndexPath.row){
            height = cellHeight;
            currentY = self.contentOffsetY + (self.collectionView.bounds.size.height)/2.0 - height/2.0;
            scale = 1;
            
        }else{
            height = cellHeight;
            currentY = self.contentOffsetY + self.cellToBottom;
            scale = 1.2;
        }
        attribute.frame = CGRectMake(centerX - cellWidth/2.0, currentY, width , height);
        CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
        attribute.transform = transform;
        attribute.zIndex = row;
        [self.cellLayoutList addObject:attribute];
    }
}

- (CGSize)collectionViewContentSize{
    return CGSizeMake(self.collectionView.frame.size.width, self.contentSizeHeight );
}

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset{
    return CGPointMake(0, self.contentOffsetY);
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSMutableArray *array = [NSMutableArray array];
    for (UICollectionViewLayoutAttributes* attribute in self.cellLayoutList) {
        if (CGRectIntersectsRect(attribute.frame, rect)) {
            [array addObject:attribute];
        }
    }
    return array;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    return [self.cellLayoutList objectAtIndex:indexPath.row];
}

@end
View Code

CardCollectionViewCell : UICollectionViewCell

#import <UIKit/UIKit.h>

@interface CardCollectionViewCell : UICollectionViewCell
@property(nonatomic, strong)NSString * title;
@property(nonatomic, strong)UIColor * bgColor;
@property(nonatomic, strong)UIImage * image;
- (void)setBlur:(CGFloat)ratio; //设置毛玻璃效果
@end
View Code
#import "CardCollectionViewCell.h"

@interface CardCollectionViewCell()
@property(nonatomic, strong)UILabel * titleLabel;
@property(nonatomic, strong)UIImageView * imageView;
@property(nonatomic, strong)UIVisualEffectView * blurView;
@end

static int cellCount;
@implementation CardCollectionViewCell

- (instancetype)init{
    self = [self initWithFrame:CGRectZero];
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self initUI];
        cellCount++;
        NSLog(@"%d",cellCount);
    }
    return self;
}

- (void)layoutSubviews{
    self.contentView.frame = self.bounds;
    self.titleLabel.center = CGPointMake(self.bounds.size.width/2.0, 2 + self.titleLabel.frame.size.height/2.0);
    self.imageView.frame = self.bounds;
    self.blurView.frame = self.bounds;
}

- (void)initUI{
    [self.contentView addSubview:self.titleLabel];
    self.layer.cornerRadius = 6;
    self.layer.masksToBounds = YES;
}

- (UILabel *)titleLabel{
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc]init];
        _titleLabel.font = [UIFont systemFontOfSize:14];
        _titleLabel.textColor = [UIColor whiteColor];
    }
    return _titleLabel;
}

- (void)setTitle:(NSString *)title{
    _title = title;
    self.titleLabel.text = title;
    [self.titleLabel sizeToFit];
    [self setNeedsLayout];
}

- (void)setBgColor:(UIColor *)bgColor{
    self.contentView.backgroundColor = bgColor;
}

- (void)setImage:(UIImage *)image{
    _image = image;
    [self.imageView removeFromSuperview];
    self.imageView = [[UIImageView alloc]initWithImage:image];
    [self.contentView addSubview:self.imageView];
}

//设置毛玻璃效果
- (void)setBlur:(CGFloat)ratio{
    if (!self.blurView.superview) {
        [self.contentView addSubview:self.blurView];
    }
    [self.contentView bringSubviewToFront:self.blurView];
    self.blurView.alpha = ratio;
}

- (UIVisualEffectView *)blurView{
    if (!_blurView) {
        _blurView = [[UIVisualEffectView alloc]initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
        _blurView.frame = self.bounds;
    }
    return _blurView;
}

@end
View Code

 9、

UIViewController

#import "ViewController9.h"
#import "FlowLayout9.h"
#import "CarouselViewModel.h"
#import "CarouselUIService.h"

@interface ViewController9 ()
@property (nonatomic, strong) UICollectionView * collectionView;
@property (nonatomic, strong) UILabel * indexLabel;
@property (nonatomic, assign) NSInteger allCount;
@property (nonatomic, strong) CarouselViewModel * viewModel;
@property (nonatomic, strong) CarouselUIService * service;
@end

@implementation ViewController9

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.8];
    self.automaticallyAdjustsScrollViewInsets = NO;
    
    [self.view addSubview:self.collectionView];
    [self.viewModel getData];
    [self.collectionView reloadData];
    
    _indexLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.collectionView.frame), self.view.frame.size.width, 20)];
    _indexLabel.textAlignment = NSTextAlignmentCenter;
    _indexLabel.font = [UIFont systemFontOfSize:13];
    _allCount = [self.viewModel.data count];
    _indexLabel.text = [NSString stringWithFormat:@"浏览记录(2/%li)",_allCount];
    [self.view addSubview:_indexLabel];
}

#pragma mark - lazy load

- (UICollectionView *)collectionView{
    if (!_collectionView) {
        FlowLayout9 * layout = [[FlowLayout9 alloc] init];
        __weak typeof (self)weakSelf = self;
        layout.SlideIndexBlock = ^(NSInteger index){
            weakSelf.indexLabel.text = [NSString stringWithFormat:@"浏览足迹(%li/%li)",index+1,_allCount];
        };
        layout.itemSize = CGSizeMake(190, 262);
        
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 270) collectionViewLayout:layout];
        _collectionView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:0.5];
        _collectionView.dataSource = self.service;
        _collectionView.delegate = self.service;
        _collectionView.showsHorizontalScrollIndicator = NO;
        _collectionView.showsVerticalScrollIndicator = NO;
        [_collectionView registerNib:[UINib nibWithNibName:@"CarouseCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:@"CarouseCollectionViewCell"];
    }
    return _collectionView;
}

- (CarouselViewModel *)viewModel{
    
    if (!_viewModel) {
        _viewModel = [[CarouselViewModel alloc] init];
    }
    return _viewModel;
}

- (CarouselUIService *)service{
    
    if (!_service) {
        _service = [[CarouselUIService alloc] init];
        _service.viewModel = self.viewModel;
    }
    return _service;
}
View Code

UICollectionViewLayout

#import <UIKit/UIKit.h>

typedef void(^SlideIndexBlock)(NSInteger index);

@interface FlowLayout9 : UICollectionViewLayout
@property (nonatomic, copy) SlideIndexBlock SlideIndexBlock;
@property (nonatomic) NSInteger visibleCount;
@property (nonatomic) CGSize itemSize;
@end
View Code
#import "FlowLayout9.h"

@implementation FlowLayout9
{
    CGFloat _viewHeight;
    CGFloat _itemHeight;
    CGFloat _DefaultInsetLeft;
}

/**
 *  覆写prepareLayout,储存布局信息
 */
- (void)prepareLayout{
    [super prepareLayout];
    self.visibleCount = self.visibleCount < 1?5:self.visibleCount;
    _viewHeight = CGRectGetWidth(self.collectionView.frame);
    _itemHeight = self.itemSize.width;
    //初始状态
    _DefaultInsetLeft = _DefaultInsetLeft == 0?-(_viewHeight - _itemHeight)/ 2:_DefaultInsetLeft;
    self.collectionView.contentInset = UIEdgeInsetsMake(0,  _DefaultInsetLeft, 0, (_viewHeight - _itemHeight) / 2);
}

/**
 *  储存视图内容
 *
 *  @param indexPath indexpath
 *
 *  @return frame、size、apha、hiden
 */
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
    UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attributes.size = self.itemSize;
    CGFloat cY = (self.collectionView.contentOffset.x) + _viewHeight / 2;
    CGFloat attributesY = _itemHeight * indexPath.row + _itemHeight / 2;
    attributes.zIndex = -ABS(attributesY - cY);
    
    CGFloat delta = cY - attributesY;
    CGFloat ratio =  - delta / (_itemHeight * 2);
    CGFloat scale = 1 - ABS(delta) / (_itemHeight * 6.0) * cos(ratio * M_PI_4);
    
    attributes.alpha = scale;
    attributes.transform = CGAffineTransformMakeScale(scale, scale);
    CGFloat centerY = attributesY;
    attributes.center = CGPointMake(centerY, CGRectGetHeight(self.collectionView.frame) / 2);
    return attributes;
}

/**
 *  返回内容尺寸
 *
 *  @return 内容尺寸
 */
- (CGSize)collectionViewContentSize{
    NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
    return CGSizeMake(cellCount * _itemHeight, CGRectGetHeight(self.collectionView.frame));
}

/**
 *  指定的区域显示cell、SupplementaryView和DecorationView中哪些视图
 *
 *  @param rect rect
 *
 *  @return 返回一组UICollectionViewLayoutAttributes类型对象
 */
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
    CGFloat centerY = ( self.collectionView.contentOffset.x) + _viewHeight / 2;
    NSInteger index = centerY / _itemHeight;
    NSInteger count = (self.visibleCount - 1) / 2;
    NSInteger minIndex = MAX(0, (index - count));
    NSInteger maxIndex = MIN((cellCount - 1), (index + count));
    NSMutableArray * array = [NSMutableArray array];
    for (NSInteger i = minIndex; i <= maxIndex; i++) {
        NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes * attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [array addObject:attributes];
    }
    return array;
}

// return a point at which to rest after scrolling - for layouts that want snap-to-point scrolling behavior 重载第四个属性,item自动中心对齐
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    CGFloat index = roundf((( proposedContentOffset.x) + _viewHeight / 2 - _itemHeight / 2) / _itemHeight);
    proposedContentOffset.x = _itemHeight * index + _itemHeight / 2 - _viewHeight / 2;
    if (self.SlideIndexBlock) {
        self.SlideIndexBlock((NSInteger)index);
    }
    _DefaultInsetLeft = (_viewHeight - _itemHeight)/ 2;
    return proposedContentOffset;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return !CGRectEqualToRect(newBounds, self.collectionView.bounds);
}

@end
View Code

CarouselViewModel

#import <Foundation/Foundation.h>

@interface CarouselViewModel : NSObject
@property (nonatomic, strong) NSMutableArray * data;
- (void)getData;
@end
View Code
#import "CarouselViewModel.h"
#import "CarouseModel.h"

@implementation CarouselViewModel
- (void)getData{
    NSInteger count = 20;
    NSMutableArray * data = [[NSMutableArray alloc] initWithCapacity:count];
    int frakeIndex = 0;
    for (int i = 0; i<count; i++) {
        CarouseModel * model = [[CarouseModel alloc] init];
        model.p_price = 10.0 + i;
        model.p_name = [NSString stringWithFormat:@"%@这是一款商品这是一款商品这是一款商品",@(i)];
        model.p_imageURL = [NSString stringWithFormat:@"pic_%d.jpg",frakeIndex];
        frakeIndex++;
        frakeIndex = frakeIndex > 3 ? 0:frakeIndex;
        [data addObject:model];
    }
    self.data = data;
}
@end
View Code

CarouselUIService

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class CarouselViewModel;

@interface CarouselUIService : NSObject<UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) CarouselViewModel * viewModel;
@end
View Code
#import "CarouselUIService.h"
#import "CarouseCollectionViewCell.h"
#import "CarouselViewModel.h"
#import "CarouseModel.h"

@implementation CarouselUIService

#pragma mark - UICollectionView Delegate / DataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView
     numberOfItemsInSection:(NSInteger)section {
    return self.viewModel.data.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    CarouseCollectionViewCell * cell= [collectionView dequeueReusableCellWithReuseIdentifier:@"CarouseCollectionViewCell" forIndexPath:indexPath];
    CarouseModel * model = self.viewModel.data[indexPath.row];
    cell.model = model;
    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"%ld",indexPath.row);
}

@end
View Code

CarouseModel

#import <Foundation/Foundation.h>

@interface CarouseModel : NSObject
@property (nonatomic, strong) NSString * p_name;
@property (nonatomic, strong) NSString * p_imageURL;
@property (nonatomic, assign) float p_price;
@end
View Code
#import "CarouseModel.h"

@implementation CarouseModel

@end
View Code

UICollectionViewCell

#import <UIKit/UIKit.h>
@class CarouseModel;

@interface CarouseCollectionViewCell : UICollectionViewCell
@property (nonatomic, strong) CarouseModel * model;
@end
View Code
#import "CarouseCollectionViewCell.h"
#import "CarouseModel.h"

@interface CarouseCollectionViewCell()
@property (weak, nonatomic) IBOutlet UIImageView * goodImageView;
@property (weak, nonatomic) IBOutlet UILabel * goodNameLabel;
@property (weak, nonatomic) IBOutlet UILabel * goodPriceLabel;
@end

@implementation CarouseCollectionViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    // Initialization code
}

- (void)setModel:(CarouseModel *)model{
    _model = model;
    _goodImageView.image = [UIImage imageNamed:model.p_imageURL];
    _goodNameLabel.text = model.p_name;
    _goodPriceLabel.text = [NSString stringWithFormat:@"¥%0.2f",model.p_price];
}

@end
View Code

10、

UIViewController

#import "ViewController10.h"
#import "GridListCollectionViewCell.h"
#import "GridListModel.h"
#import "NSObject+Property.h"
#define ScreenWidth ([UIScreen mainScreen].bounds.size.width)

@interface ViewController10 ()<UICollectionViewDelegate, UICollectionViewDataSource>
@property (nonatomic, strong) UICollectionView * collectionView;
@property (nonatomic, strong) NSMutableArray * dataSource;

@property (weak, nonatomic) IBOutlet UIButton *swithBtn;

@end

@implementation ViewController10
{
    BOOL _isGrid;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // 默认列表视图
    _isGrid = NO;
    [self.view addSubview:self.collectionView];
    NSString * path = [[NSBundle mainBundle] pathForResource:@"product" ofType:@"json"];
    NSData * data = [NSData dataWithContentsOfFile:path];
    //NSJSONReadingMutableContainers:返回可变容器,NSMutableDictionary或NSMutableArray。
    //NSJSONReadingMutableLeaves:返回的JSON对象中字符串的值为NSMutableString,目前在iOS 7上测试不好用,应该是个bug,参见:http://stackoverflow.com/questions/19345864/nsjsonreadingmutableleaves-option-is-not-working
    //NSJSONReadingAllowFragments:允许JSON字符串最外层既不是NSArray也不是NSDictionary,但必须是有效的JSON Fragment。例如使用这个选项可以解析 @“123” 这样的字符串。参见:http://stackoverflow.com/questions/16961025/nsjsonserialization-nsjsonreadingallowfragments-reading
    NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
    NSArray * products = dict[@"wareInfo"];
    for (id obj in products) {
        [self.dataSource addObject:[GridListModel objectWithDictionary:obj]];
    }
}

- (NSMutableArray *)dataSource{
    if (!_dataSource) {
        _dataSource = [NSMutableArray array];
    }
    return _dataSource;
}

- (UICollectionView *)collectionView{
    if (!_collectionView) {
        UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
        layout.scrollDirection = UICollectionViewScrollDirectionVertical;
        layout.minimumInteritemSpacing = 2;
        layout.minimumLineSpacing = 2;
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(2, 2, self.view.bounds.size.width - 4, self.view.bounds.size.height - 4) collectionViewLayout:layout];
        _collectionView.delegate = self;
        _collectionView.dataSource = self;
        _collectionView.showsVerticalScrollIndicator = NO;
        _collectionView.showsHorizontalScrollIndicator = NO;
        [_collectionView setBackgroundColor:[UIColor clearColor]];
        [_collectionView registerClass:[GridListCollectionViewCell class] forCellWithReuseIdentifier:CellIdentifier];
    }
    return _collectionView;
}

#pragma  mark - UICollectionView Delegate

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.dataSource.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    GridListCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
    cell.isGrid = _isGrid;
    cell.model = self.dataSource[indexPath.row];
    return cell;
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    if (_isGrid) {
        return CGSizeMake((ScreenWidth - 6) / 2, (ScreenWidth - 6) / 2 + 40);
    } else {
        return CGSizeMake(ScreenWidth - 4, (ScreenWidth - 6) / 4 + 20);
    }
}
- (IBAction)onBtnClick:(UIButton *)sender {
    _isGrid = !_isGrid;
    [self.collectionView reloadData];
    
    if (_isGrid) {
        [self.swithBtn setImage:[UIImage imageNamed:@"product_list_grid_btn"] forState:UIControlStateNormal];
    } else {
        [self.swithBtn setImage:[UIImage imageNamed:@"product_list_list_btn"] forState:UIControlStateNormal];
    }
}
View Code

UICollectionViewCell

#import <UIKit/UIKit.h>
#define CellIdentifier @"GridListCollectionViewCell"
@class GridListModel;

@interface GridListCollectionViewCell : UICollectionViewCell
/**
 0:列表视图,1:格子视图
 */
@property (nonatomic, assign) BOOL isGrid;
@property (nonatomic, strong) GridListModel * model;
@end
View Code
#import "GridListCollectionViewCell.h"
#import "GridListModel.h"
#import "UIImageView+WebCache.h"
#define ScreenWidth ([UIScreen mainScreen].bounds.size.width)

@interface GridListCollectionViewCell()
@property (nonatomic, strong) UIImageView * imageV;
@property (nonatomic, strong) UILabel * titleLabel;
@property (nonatomic, strong) UILabel * priceLabel;
@end

@implementation GridListCollectionViewCell
- (instancetype)initWithFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
        [self configureUI];
    }
    return self;
}

- (void)configureUI{
    _imageV = [[UIImageView alloc] initWithFrame:CGRectZero];
    [self.contentView addSubview:_imageV];
    
    _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    _titleLabel.numberOfLines = 0;
    _titleLabel.font = [UIFont boldSystemFontOfSize:14];
    [self.contentView addSubview:_titleLabel];
    
    _priceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    _priceLabel.textColor = [UIColor redColor];
    _priceLabel.font = [UIFont systemFontOfSize:16];
    [self.contentView addSubview:_priceLabel];
}

- (void)setIsGrid:(BOOL)isGrid{
    _isGrid = isGrid;
    if (isGrid) {
        _imageV.frame = CGRectMake(5, 5, self.bounds.size.width - 60, self.bounds.size.width - 60);
        _titleLabel.frame = CGRectMake(5, self.bounds.size.width - 45, ScreenWidth/2, 20);
        _priceLabel.frame = CGRectMake(5, self.bounds.size.width - 20, ScreenWidth/2, 20);
    } else {
        _imageV.frame = CGRectMake(5, 5, self.bounds.size.height - 10, self.bounds.size.height - 10);
        _titleLabel.frame = CGRectMake(self.bounds.size.height + 10, 0, ScreenWidth/2, self.bounds.size.height - 20);;
        _priceLabel.frame = CGRectMake(self.bounds.size.height + 10, self.bounds.size.height - 30, ScreenWidth/2, 20);;
    }
}

- (void)setModel:(GridListModel *)model{
    _model = model;
    [_imageV sd_setImageWithURL:[NSURL URLWithString:model.imageurl]];
    _titleLabel.text = model.wname;
    _priceLabel.text = [NSString stringWithFormat:@"¥%.2f",model.jdPrice];
}

@end
View Code

Model

#import <Foundation/Foundation.h>

@interface GridListModel : NSObject
@property (nonatomic, strong) NSString * imageurl;
@property (nonatomic, strong) NSString * wname;
@property (nonatomic, assign) float jdPrice;
@property (nonatomic, assign) int totalCount;
@end
View Code
#import "GridListModel.h"

@implementation GridListModel

@end
View Code

NSObject+Property.h

#import <Foundation/Foundation.h>

@protocol KeyValue <NSObject>
@optional
/**
 *  数组中需要转换的模型类
 *
 *  @return 字典中的key是数组属性名,value是数组中存放模型的Class(Class类型或者NSString类型)
 */
+ (NSDictionary *)objectClassInArray;

/**
 *  将属性名换为其他key去字典中取值
 *
 *  @return 字典中的key是属性名,value是从字典中取值用的key
 */
+ (NSDictionary *)replacedKeyFromPropertyName;
@end

@interface NSObject (Property) <KeyValue>
+ (instancetype)objectWithDictionary:(NSDictionary *)dictionary;
@end
View Code
#import "NSObject+Property.h"
#import <objc/runtime.h>

@implementation NSObject (Property)
+ (instancetype)objectWithDictionary:(NSDictionary *)dictionary
{
    id obj = [[self alloc] init];
    
    // 获取所有的成员变量
    unsigned int count;
    Ivar * ivars = class_copyIvarList(self, &count);
    
    for (unsigned int i = 0; i < count; i++)
    {
        Ivar ivar = ivars[i];
        
        // 取出的成员变量,去掉下划线
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        NSString *key = [ivarName substringFromIndex:1];
        
        id value = dictionary[key];
        
        // 当这个值为空时,判断一下是否执行了replacedKeyFromPropertyName协议,如果执行了替换原来的key查值
        if (!value)
        {
            if ([self respondsToSelector:@selector(replacedKeyFromPropertyName)])
            {
                NSString *replaceKey = [self replacedKeyFromPropertyName][key];
                value = dictionary[replaceKey];
            }
        }
        
        // 字典嵌套字典
        if ([value isKindOfClass:[NSDictionary class]])
        {
            NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
            NSRange range = [type rangeOfString:@"""];
            type = [type substringFromIndex:range.location + range.length];
            range = [type rangeOfString:@"""];
            type = [type substringToIndex:range.location];
            Class modelClass = NSClassFromString(type);
            
            if (modelClass)
            {
                value = [modelClass objectWithDictionary:value];
            }
        }
        
        // 字典嵌套数组
        if ([value isKindOfClass:[NSArray class]])
        {
            if ([self respondsToSelector:@selector(objectClassInArray)])
            {
                NSMutableArray *models = [NSMutableArray array];
                
                NSString *type = [self objectClassInArray][key];
                Class classModel = NSClassFromString(type);
                for (NSDictionary *dict in value)
                {
                    id model = [classModel objectWithDictionary:dict];
                    [models addObject:model];
                }
                value = models;
            }
        }
        
        if (value)
        {
            [obj setValue:value forKey:key];
        }
    }
    
    // 释放ivars
    free(ivars);
    
    return obj;
}
@end
View Code

11、

UIViewController

#import "ViewController11.h"
#import "FMLayout.h"
#import "FMCollectionViewCell.h"
#import "MXModel.h"

@interface ViewController11 ()<UICollectionViewDelegate, UICollectionViewDataSource>
@property (nonatomic, strong) UICollectionView * collectionView;
@property (nonatomic, strong) NSMutableArray * dataSource;
@end

@implementation ViewController11

- (void)viewDidLoad {
    [super viewDidLoad];
    
    FMLayout * layout = [[FMLayout alloc] init];
    layout.itemSize = CGSizeMake([UIScreen mainScreen].bounds.size.width*2/3, [UIScreen mainScreen].bounds.size.height);
    
    self.collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
    [self.collectionView registerNib:[UINib nibWithNibName:@"FMCollectionViewCell" bundle:[NSBundle mainBundle]] forCellWithReuseIdentifier:@"CellId"];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.view addSubview:self.collectionView];
    self.collectionView.backgroundColor = [UIColor whiteColor];
    
    NSArray * mxData = [[NSArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MXData" ofType:@"plist"]];
    NSMutableArray * array = [NSMutableArray array];
    
    UIImageView * backImageView = [[UIImageView alloc] initWithFrame:self.collectionView.bounds];
    backImageView.image = [UIImage imageNamed:@"10366009.jpg"];
    self.collectionView.backgroundView = backImageView;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (NSArray * item in mxData){
            @try {
                MXModel * model = [[MXModel alloc] init];
                model.title = item[0];
                model.snap = [UIImage imageNamed:item[1]];
                model.icon = [UIImage imageNamed:item[2]];
                [array addObject:model];
            } @catch (NSException * exception) {
                NSLog(@"MXData.plist has problems");
            } @finally {
                
            }
        }
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.dataSource = array;
            [self.collectionView reloadData];
        });
    });
}


#pragma mark - UICollectionView Delegate

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
    return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return self.dataSource.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    FMCollectionViewCell * cell = (FMCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
    [self configureCell:cell withIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(FMCollectionViewCell *)cell withIndexPath:(NSIndexPath *)indexPath{
    cell.model = _dataSource[indexPath.row];
}
View Code

UICollectionViewCell

#import <UIKit/UIKit.h>
#import "MXModel.h"

@interface FMCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UIImageView * snapView;
@property (weak, nonatomic) IBOutlet UIImageView * appIcon;
@property (weak, nonatomic) IBOutlet UILabel * appNameLabel;

@property (nonatomic, strong) MXModel * model;
@end
View Code
#import "FMCollectionViewCell.h"
#import "MXKitMacro.h"
#import "NSObject+MXAddForKVO.h"

@interface FMCollectionViewCell()
@property (weak, nonatomic) IBOutlet UIVisualEffectView *snapBlurEffect;
@property (weak, nonatomic) IBOutlet UIVisualEffectView *iconBlurEffect;
@end

@implementation FMCollectionViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
    self.backgroundColor = [UIColor clearColor];
    _snapView.layer.cornerRadius = 5;
    _snapView.clipsToBounds = YES;
    _appIcon.layer.cornerRadius = 6;
    _appIcon.clipsToBounds = YES;
    
    @weakify(self)
    [self addObserverBlockForKeyPath:@"alpha" block:^(id obj, id oldVal, id newVal){
        @strongify(self)
//         self.snapBlurEffect.alpha = MIN(3 * (1 - self.alpha), 0.6);
//         self.iconBlurEffect.alpha = MIN(3 * (1 - self.alpha), 0.6);
        if (self.alpha == 1){
            self.appNameLabel.alpha = 1;
        }else{
            self.appNameLabel.alpha = self.alpha / 3;
        }
    }];
}

- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        self.backgroundColor = [UIColor clearColor];
        _snapView.layer.cornerRadius = 5;
        _snapView.clipsToBounds = YES;
    }
    return self;
}

- (void)setModel:(MXModel *)model{
    if (_model != model){
        _model = model;
        self.snapView.image = model.snap;
        self.appIcon.image = model.icon;
        self.appNameLabel.text = model.title;
    }
}

- (void)dealloc{
    [self removeObserverBlocks];
}
@end
View Code

UICollectionViewLayout

#import <UIKit/UIKit.h>

@interface FMLayout : UICollectionViewLayout
@property (nonatomic, assign) CGSize itemSize;
@end
View Code
#import "FMLayout.h"
#define MARGIN (_viewWidth / 3 - _virtualItemWidth / 2)

@interface FMLayout()
@property (nonatomic, assign) CGFloat viewWidth;
@property (nonatomic, assign) CGFloat virtualItemWidth;
@property (nonatomic, assign) NSInteger visibleCount;
@end

@implementation FMLayout

- (void)prepareLayout{
    [super prepareLayout];
    if (self.visibleCount < 1){
        self.visibleCount = 4;
    }
    self.viewWidth = CGRectGetWidth(self.collectionView.frame);
    self.virtualItemWidth = self.itemSize.width/2;
}

- (CGSize)collectionViewContentSize {
    NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
    return CGSizeMake(cellCount * _virtualItemWidth + MARGIN * 3, [UIScreen mainScreen].bounds.size.height);
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
    NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
    CGFloat centerX = (self.collectionView.contentOffset.x) + _viewWidth / 2;
    NSInteger index = centerX / _virtualItemWidth;
    NSInteger count = (self.visibleCount - 1) / 2;
    NSInteger minIndex = MAX(0, (index - count - 1));
    NSInteger maxIndex = MIN((cellCount - 1), (index + count));
    NSMutableArray * array = [NSMutableArray array];
    for (NSInteger i = minIndex; i <= maxIndex; i++) {
        NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
        [array addObject:attributes];
    }
    return array;
}

- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
    attributes.size = self.itemSize;
    
    CGFloat cX = (self.collectionView.contentOffset.x) + _viewWidth / 2;
    CGFloat attributesX = _virtualItemWidth * indexPath.row + _virtualItemWidth / 2 + MARGIN;
    attributes.zIndex = attributesX;
    
    CGFloat delta = cX - attributesX;
    
    CGFloat ratio = delta / (_virtualItemWidth);
    
    CGFloat centerX = attributesX;
    
    CGFloat offset =  pow((-ratio + 2), 3)/23 * _itemSize.width;
    centerX = cX - _viewWidth/2 + offset + _itemSize.width / 2 - 5;
    
    CGFloat scale = (-ratio + 2 - 2)/50 + 1;
    attributes.transform = CGAffineTransformMakeScale(scale, scale);//x scale has something doesn`t right
    
    if (ratio > 1.3){
        attributes.alpha = MAX(0, 2.3 - ratio);
    }
    
    attributes.center = CGPointMake(centerX, CGRectGetHeight(self.collectionView.frame) / 2);
    return attributes;
}

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    CGFloat index = roundf((proposedContentOffset.x + _viewWidth / 3 - _itemSize.width / 2) / _virtualItemWidth);
    proposedContentOffset.x = _virtualItemWidth * index + _itemSize.width / 2 - _viewWidth / 3;
    return proposedContentOffset;
}

- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    return !CGRectEqualToRect(newBounds, self.collectionView.bounds);
}

@end
View Code

Model

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface MXModel : NSObject
@property (nonatomic, strong) UIImage * snap;
@property (nonatomic, strong) UIImage * icon;
@property (nonatomic, strong) NSString * title;
@end
View Code
#import "MXModel.h"

@implementation MXModel

@end
View Code

MXKitMacro.h

#ifndef MXKitMacro_h
#define MXKitMacro_h

#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif

#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif


#endif /* MXKitMacro_h */
View Code

NSObject+MXAddForKVO

#import <Foundation/Foundation.h>

typedef void(^MXKVOBlock)(id obj, id oldVal, id newVal);

@interface NSObject (MXAddForKVO)

/**
 *  block会自动retain内部捕获的对象。removeObserverBlocksForKeyPath或者removeObserverBlocks后会释放块,并且释放捕获的对象。
 *
 */
- (void)addObserverBlockForKeyPath:(NSString*)keyPath block:(MXKVOBlock)block;

- (void)removeObserverBlocksForKeyPath:(NSString*)keyPath;

- (void)removeObserverBlocks;

@end
View Code
#import "NSObject+MXAddForKVO.h"
#import <objc/objc.h>
#import <objc/runtime.h>

static const int block_key;

@interface _MXNSObjectKVOBlockTarget : NSObject

@property (nonatomic, copy) MXKVOBlock block;

- (id)initWithBlock:(MXKVOBlock)block;

@end

@implementation _MXNSObjectKVOBlockTarget

- (id)initWithBlock:(MXKVOBlock)block
{
    self = [super init];
    if (self)
    {
        self.block = block;
    }
    
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (!self.block) return;
    
    BOOL isPrior = [[change objectForKey:NSKeyValueChangeNotificationIsPriorKey] boolValue];//不接收改变之前的值
    if (isPrior) return;
    
    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
    if (changeKind != NSKeyValueChangeSetting) return;//不接收集合类型对象的改变
    
    id oldVal = [change objectForKey:NSKeyValueChangeOldKey];
    if (oldVal == [NSNull null]) oldVal = nil;
    
    id newVal = [change objectForKey:NSKeyValueChangeNewKey];
    if (newVal == [NSNull null]) newVal = nil;
    
    self.block(object, oldVal, newVal);
}

@end



@implementation NSObject (MXAddForKVO)

- (void)addObserverBlockForKeyPath:(NSString *)keyPath block:(MXKVOBlock)block
{
    if (!keyPath || !block) return;
    _MXNSObjectKVOBlockTarget *target = [[_MXNSObjectKVOBlockTarget alloc] initWithBlock:block];
    NSMutableDictionary *dic = [self _MX_allNSObjectObserverBlocks];
    NSMutableArray *arr = dic[keyPath];
    if (!arr)
    {
        arr = [NSMutableArray new];
        dic[keyPath] = arr;
    }
    
    [arr addObject:target];
    [self addObserver:target forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:NULL];
}

- (void)removeObserverBlocksForKeyPath:(NSString *)keyPath
{
    if (!keyPath) return;
    NSMutableDictionary *dic = [self _MX_allNSObjectObserverBlocks];
    NSMutableArray *arr = dic[keyPath];
    [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
        [self removeObserver:obj forKeyPath:keyPath];
    }];
    
    [dic removeObjectForKey:keyPath];
}

- (void)removeObserverBlocks
{
    NSMutableDictionary *dic = [self _MX_allNSObjectObserverBlocks];
    [dic enumerateKeysAndObjectsUsingBlock: ^(NSString *key, NSArray *arr, BOOL *stop){
        [arr enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) {
            [self removeObserver:obj forKeyPath:key];
        }];
    }];
    
    [dic removeAllObjects];
}

- (NSMutableDictionary *)_MX_allNSObjectObserverBlocks
{
    NSMutableDictionary *targets = objc_getAssociatedObject(self, &block_key);
    if (!targets)
    {
        targets = [NSMutableDictionary new];
        objc_setAssociatedObject(self, &block_key, targets, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    return targets;
}

@end
View Code

UIImage+MXAdd

#import <UIKit/UIKit.h>

@interface UIImage (MXAdd)

+ (UIImage *)resizeImage:(UIImage*)image toViewSize:(CGSize)size;
+ (UIImage *)simpleResizeImage:(UIImage*)image toSize:(CGSize)size;

@end
View Code
#import "UIImage+MXAdd.h"

@implementation UIImage (MXAdd)

+ (UIImage *)resizeImage:(UIImage*)image toViewSize:(CGSize)size{
    CGSize imageSize = image.size;
    CGSize toSize = CGSizeMake(round(size.width*[UIScreen mainScreen].scale/image.scale), round(size.height*[UIScreen mainScreen].scale/image.scale));
    
    UIImage *scaledImage = image;
    if (!CGSizeEqualToSize(imageSize, toSize))
    {
        
        CGRect drawRect = CGRectMake(0, 0, toSize.width, toSize.height);
        if (imageSize.width/imageSize.height > toSize.width/toSize.height)
        {
            //宽图
            drawRect.origin.x = -round((imageSize.width/imageSize.height - toSize.width/toSize.height) / 2 * toSize.height);
            drawRect.size.width = round(imageSize.width/imageSize.height * toSize.height);
            
        }else
        {
            //高图
            drawRect.origin.y = -round((imageSize.height/imageSize.width - toSize.height/toSize.width) / 2 * toSize.width);
            drawRect.size.height = round(imageSize.height/imageSize.width * toSize.width);
        }
        
        UIGraphicsBeginImageContext(toSize);
        [image drawInRect:drawRect];
        scaledImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }
    
    
    return scaledImage;
}

+ (UIImage *)simpleResizeImage:(UIImage*)image toSize:(CGSize)size{    
    CGRect rect={0,0,size};
    UIGraphicsBeginImageContextWithOptions(size, NO, 0);
    [image drawInRect:rect];
    UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return resultImage;
}

@end
View Code
原文地址:https://www.cnblogs.com/fengmin/p/6221352.html