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(, );
_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 ;
} - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:arc4random()%/225.0 green:arc4random()%/225.0 blue:arc4random()%/225.0 alpha:];
return cell;
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
2、
继承系统自动布局UICollectionViewFlowLayout。
布局FlowLayout2
#import <UIKit/UIKit.h> @interface FlowLayout2 : UICollectionViewFlowLayout
@property (nonatomic, assign) NSInteger itemCount;
@end
#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)/; CGFloat kHeight[] = {self.sectionInset.top, self.sectionInset.bottom}; for (int i = ; i < _itemCount; i++) {
NSIndexPath * index = [NSIndexPath indexPathForItem:i inSection:];
UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:index]; CGFloat rHeight = arc4random()%+;
NSInteger rWidth = ; if (kHeight[] < kHeight[]) {
kHeight[] = kHeight[] + rHeight + self.minimumLineSpacing;
rWidth = ;
}else{
kHeight[] = kHeight[] + rHeight + self.minimumLineSpacing;
rWidth = ;
} attributes.frame = CGRectMake(self.sectionInset.left + (self.minimumInteritemSpacing + kWidth)*rWidth, kHeight[rWidth] - rHeight - self.minimumLineSpacing, kWidth, rHeight);
[_attributeArray addObject:attributes];
} if (kHeight[] > kHeight[]) {
self.itemSize = CGSizeMake(kWidth, (kHeight[] - self.sectionInset.top)*/_itemCount - self.minimumLineSpacing);
}else{
self.itemSize = CGSizeMake(kWidth, (kHeight[] - self.sectionInset.top)*/_itemCount - self.minimumLineSpacing);
}
} - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return _attributeArray;
}
@end
控制器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 = ;
layout.scrollDirection = UICollectionViewScrollDirectionVertical;
layout.itemSize = CGSizeMake(, ); _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 ;
} - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:arc4random()%/255.0 green:arc4random()%/255.0 blue:arc4random()%/255.0 alpha:];
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.
}
3、
继承系统UICollectionViewLayout
#import <UIKit/UIKit.h> @interface FlowLayout3 : UICollectionViewLayout
@property (nonatomic,assign) int itemCount;
@end
#import "FlowLayout3.h" @interface FlowLayout3()
{
NSMutableArray * _attributeArray;
}
@end @implementation FlowLayout3
- (void)prepareLayout{
_itemCount = (int)[self.collectionView numberOfItemsInSection:];
_attributeArray = [NSMutableArray array];
CGFloat radius = MIN(self.collectionView.frame.size.width, self.collectionView.frame.size.height)/;
CGPoint center = CGPointMake(self.collectionView.frame.size.width/, self.collectionView.frame.size.height/);
for (int i = ; i < _itemCount; i++) {
UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:[NSIndexPath indexPathForItem:i inSection:]];
attributes.size = CGSizeMake(, );
CGFloat x = center.x + cosf(*M_PI/_itemCount*i)*(radius-);
CGFloat y = center.y + sinf(*M_PI/_itemCount*i)*(radius-);
attributes.center = CGPointMake(x, y);
[_attributeArray addObject:attributes];
}
} -(NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
return _attributeArray;
} - (CGSize)collectionViewContentSize{
return self.collectionView.frame.size;
}
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 ;
} -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:arc4random()%/255.0 green:arc4random()%/255.0 blue:arc4random()%/255.0 alpha:];
cell.layer.masksToBounds = YES;
cell.layer.cornerRadius = ;
return cell;
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
4、
UICollectionViewLayout
#import <UIKit/UIKit.h> @interface FlowLayout4 : UICollectionViewLayout @end
#import "FlowLayout4.h" @implementation FlowLayout4 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
//创建一个item布局属性类
UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
//获取item的个数
int itemCounts = (int)[self.collectionView numberOfItemsInSection:];
//设置每个item的大小为260*100
attributes.size = CGSizeMake(, ); attributes.center = CGPointMake(self.collectionView.frame.size.width/, self.collectionView.frame.size.height/ + self.collectionView.contentOffset.y);
CATransform3D tran3d = CATransform3DIdentity;
tran3d.m34 = -/2000.0;
CGFloat radius = /tanf(M_PI*/itemCounts/);
// 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 - )/itemCounts*M_PI*;
tran3d = CATransform3DRotate(tran3d, angle, 1.0, , );
tran3d = CATransform3DTranslate(tran3d, , , radius);
//进行设置
attributes.transform3D = tran3d;
return attributes;
} - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray * attributes = [[NSMutableArray alloc] init];
//遍历设置每个item的布局属性
for (int i=; i < [self.collectionView numberOfItemsInSection:]; i++) {
[attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:]]];
}
return attributes;
} - (CGSize)collectionViewContentSize{
return CGSizeMake(self.collectionView.frame.size.width, self.collectionView.frame.size.height * ([self.collectionView numberOfItemsInSection:] + ));
} //返回yes,则一有变化就会刷新布局
-(BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
} - (void)prepareLayout{ }
UIViewController
#import "ViewController4.h"
#import "FlowLayout4.h" @interface ViewController4 ()<UICollectionViewDelegateFlowLayout, UICollectionViewDataSource>
{
UICollectionView * _collectionView;
NSInteger i;
}
@end @implementation ViewController4 - (void)viewDidLoad {
[super viewDidLoad];
i = ;
FlowLayout4 * layout = [[FlowLayout4 alloc] init];
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(, , , ) 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 + ;
_collectionView.contentOffset = CGPointMake( , i);
} - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return ;
} - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:arc4random()%/255.0 green:arc4random()%/255.0 blue:arc4random()%/255.0 alpha:];
UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(, , , )];
label.text = [NSString stringWithFormat:@"我是第%ld行",(long)indexPath.row];
[cell.contentView addSubview:label];
return cell;
} -(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//小于半屏 则放到最后一屏多半屏
if (scrollView.contentOffset.y < ) {
scrollView.contentOffset = CGPointMake(, scrollView.contentOffset.y+*);
//大于最后一屏多一屏 放回第一屏
}else if(scrollView.contentOffset.y > * ){
scrollView.contentOffset = CGPointMake(, scrollView.contentOffset.y-*);
}
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
5、
UICollectionViewLayout
#import "FlowLayout5.h" @implementation FlowLayout5 - (void)prepareLayout{
[super prepareLayout];
} - (CGSize)collectionViewContentSize{
return CGSizeMake(self.collectionView.frame.size.width*([self.collectionView numberOfItemsInSection:]+), self.collectionView.frame.size.height*([self.collectionView numberOfItemsInSection:]+));
} - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
return YES;
} - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
NSMutableArray * attributes = [NSMutableArray array];
for (int i = ; i < [self.collectionView numberOfItemsInSection:]; i++) {
//遍历设置每个item的布局属性
[attributes addObject:[self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:]]];
}
return attributes;
} - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewLayoutAttributes * attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSInteger itemCount = [self.collectionView numberOfItemsInSection:];
attributes.center = CGPointMake(self.collectionView.frame.size.width/+self.collectionView.contentOffset.x, self.collectionView.frame.size.height/+self.collectionView.contentOffset.y);
attributes.size = CGSizeMake(, );
CATransform3D trans3D = CATransform3DIdentity;
trans3D.m34 = -/900.0;
CGFloat radius = /tanf(M_PI*/itemCount/); //根据偏移量,改变角度
//添加了一个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 - )/itemCount * M_PI*;
CGFloat angleX = (indexPath.item + angleOffsetX - )/itemCount * M_PI*; //四个方向的排列
if (indexPath.item % == ) {
trans3D = CATransform3DRotate(trans3D, angleY, 1.0, , );
}else if (indexPath.row % == ){
trans3D = CATransform3DRotate(trans3D, angleX, , 1.0, );
}else if (indexPath.row % == ){
trans3D = CATransform3DRotate(trans3D, angleY, 0.5, 0.5, );
}else{
trans3D = CATransform3DRotate(trans3D, angleY, 0.5, -0.5, );
} trans3D = CATransform3DTranslate(trans3D, , , radius);
attributes.transform3D = trans3D;
return attributes;
}
@end
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(, , , ) 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()%(*), arc4random()%(*));
} -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return ;
} -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CellId" forIndexPath:indexPath];
cell.backgroundColor = [UIColor colorWithRed:arc4random()%/255.0 green:arc4random()%/255.0 blue:arc4random()%/255.0 alpha:];
UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(, , , )];
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.
}
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 = ; i <= ; 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 ;
} - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
if (section == ) {
return ;
}
return _photosArray.count;
} - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == ) {
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 == ) {
return;
}
if (_photosArray.count == ) {
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 .f;
} - (CGFloat)minimumInteritemSpacingForCollectionView:(UICollectionView *)collectionView {
return .f;
} - (CGFloat)minimumLineSpacingForCollectionView:(UICollectionView *)collectionView {
return .f;
} - (UIEdgeInsets)insetsForCollectionView:(UICollectionView *)collectionView {
return UIEdgeInsetsMake(.f, , .f, );
} - (CGSize)collectionView:(UICollectionView *)collectionView sizeForLargeItemsInSection:(NSInteger)section {
if (section == ) {
return CGSizeMake(, );
}
return CGSizeZero; //same as default !
} #pragma mark - CollectionViewResetFlowLayoutDelegate - (UIEdgeInsets)autoScrollTrigerEdgeInsets:(UICollectionView *)collectionView {
return UIEdgeInsetsMake(.f, , .f, ); //Sorry, horizontal scroll is not supported now.
} - (UIEdgeInsets)autoScrollTrigerPadding:(UICollectionView *)collectionView {
return UIEdgeInsetsMake(.f, , , );
} - (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 == ) {
return NO;
}
return YES;
} - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == ) {
return NO;
}
return YES;
} - (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
UICollectionViewCell
#import <UIKit/UIKit.h> @interface CollectionViewCell6 : UICollectionViewCell
@property (nonatomic, strong) UIImageView * imageView;
@end
#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 = .f;
}
} @end
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
#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 = ;
_lineSpacing = ;
_sectionSpacing = ;
_insets = UIEdgeInsetsMake(, , , );
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 = ;
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 = ;
if ([self.delegate respondsToSelector:@selector(sectionSpacingForCollectionView:)]) {
sectionSpacing = [self.delegate sectionSpacingForCollectionView:self.collectionView];
}
CGFloat itemSpacing = ;
if ([self.delegate respondsToSelector:@selector(minimumInteritemSpacingForCollectionView:)]) {
itemSpacing = [self.delegate minimumInteritemSpacingForCollectionView:self.collectionView];
}
CGFloat lineSpacing = ;
if ([self.delegate respondsToSelector:@selector(minimumLineSpacingForCollectionView:)]) {
lineSpacing = [self.delegate minimumLineSpacingForCollectionView:self.collectionView];
} contentHeight += insets.top + insets.bottom + sectionSpacing * (numberOfSections - ); CGFloat lastSmallCellHeight = ;
for (NSInteger i = ; i < numberOfSections; i++) {
NSInteger numberOfLines = ceil((CGFloat)[self.collectionView numberOfItemsInSection:i] / .f); CGFloat largeCellSideLength = (.f * (collectionViewSize.width - insets.left - insets.right) - itemSpacing) / .f;
CGFloat smallCellSideLength = (largeCellSideLength - itemSpacing) / .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 / .f) - (itemSpacing / .f));
}
}
lastSmallCellHeight = smallCellSize.height;
CGFloat largeCellHeight = largeCellSize.height;
CGFloat lineHeight = numberOfLines * (largeCellHeight + lineSpacing) - lineSpacing;
contentHeight += lineHeight;
} NSInteger numberOfItemsInLastSection = [self.collectionView numberOfItemsInSection:numberOfSections -];
if ((numberOfItemsInLastSection - ) % == && (numberOfItemsInLastSection - ) % != ) {
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, );
for (NSInteger i = ; i < self.collectionView.numberOfSections; i++) {
if ([self.collectionView numberOfItemsInSection:i] == ) {
break;
}
NSInteger numberOfLines = ceil((CGFloat)[self.collectionView numberOfItemsInSection:i] / .f);
CGFloat lineHeight = numberOfLines * ([_largeCellSizeArray[i] CGSizeValue].height + _lineSpacing) - _lineSpacing;
contentSize.height += lineHeight;
}
contentSize.height += _insets.top + _insets.bottom + _sectionSpacing * (self.collectionView.numberOfSections - );
NSInteger numberOfItemsInLastSection = [self.collectionView numberOfItemsInSection:self.collectionView.numberOfSections - ];
if ((numberOfItemsInLastSection - ) % == && (numberOfItemsInLastSection - ) % != ) {
contentSize.height -= [_smallCellSizeArray[self.collectionView.numberOfSections - ] CGSizeValue].height + _itemSpacing;
}
return contentSize;
} - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
_oldRect = rect;
NSMutableArray *attributesArray = [NSMutableArray array];
for (NSInteger i = ; i < self.collectionView.numberOfSections; i++) {
NSInteger numberOfCellsInSection = [self.collectionView numberOfItemsInSection:i];
for (NSInteger j = ; 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 = (.f * (_collectionViewSize.width - _insets.left - _insets.right) - _itemSpacing) / .f;
CGFloat smallCellSideLength = (largeCellSideLength - _itemSpacing) / .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 / .f) - (_itemSpacing / .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 = ;
for (NSInteger i = ; i <= indexPath.section - ; i++) {
NSInteger cellsCount = [self.collectionView numberOfItemsInSection:i];
CGFloat largeCellHeight = [_largeCellSizeArray[i] CGSizeValue].height;
CGFloat smallCellHeight = [_smallCellSizeArray[i] CGSizeValue].height;
NSInteger lines = ceil((CGFloat)cellsCount / .f);
sectionHeight += lines * (_lineSpacing + largeCellHeight) + _sectionSpacing;
if ((cellsCount - ) % == && (cellsCount - ) % != ) {
sectionHeight -= smallCellHeight + _itemSpacing;
}
}
if (sectionHeight > ) {
sectionHeight -= _lineSpacing;
} NSInteger line = indexPath.item / ;
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 % == ) {
attribute.frame = CGRectMake(_insets.left, lineOriginY, _largeCellSize.width, _largeCellSize.height);
}else if ((indexPath.item + ) % == ) {
attribute.frame = CGRectMake(rightSideLargeCellOriginX, lineOriginY, _largeCellSize.width, _largeCellSize.height);
}else if (line % == ) {
if (indexPath.item % != ) {
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 % != ) {
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
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
#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, .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(.f, .f, .f, .f);
if ([self.delegate respondsToSelector:@selector(autoScrollTrigerEdgeInsets:)]) {
_scrollTrigerEdgeInsets = [self.delegate autoScrollTrigerEdgeInsets:self.collectionView];
}
//scroll triger padding
_scrollTrigePadding = UIEdgeInsetsMake(, , , );
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 = ;
if ([self.delegate respondsToSelector:@selector(reorderingItemAlpha:)]) {
alpha = [self.delegate reorderingItemAlpha:self.collectionView];
if (alpha >= .f) {
alpha = .f;
}else if (alpha <= ) {
alpha = ;
}
}
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(, );
_cellFakeView.layer.shadowOpacity = .5f;
_cellFakeView.layer.shadowRadius = .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 / .f), cell.center.y - (self.smallCellSize.height / .f), self.smallCellSize.width, self.smallCellSize.height);
[UIView animateWithDuration:.3f delay: options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseInOut animations:^{
_cellFakeView.center = cell.center;
_cellFakeView.frame = fakeViewRect;
_cellFakeView.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
highlightedImageView.alpha = ;
} 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: 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 = ; if (self.scrollDirection == RAScrollDirctionDown) {
CGFloat percentage = (((CGRectGetMaxY(_cellFakeView.frame) - contentOffset.y) - (boundsSize.height - _scrollTrigerEdgeInsets.bottom - _scrollTrigePadding.bottom)) / _scrollTrigerEdgeInsets.bottom);
increment = * percentage;
if (increment >= .f) {
increment = .f;
}
}else if (self.scrollDirection == RAScrollDirctionUp) {
CGFloat percentage = (.f - ((CGRectGetMinY(_cellFakeView.frame) - contentOffset.y - _scrollTrigePadding.top) / _scrollTrigerEdgeInsets.top));
increment = -.f * percentage;
if (increment <= -.f) {
increment = -.f;
}
} if (contentOffset.y + increment <= -contentInset.top) {
[UIView animateWithDuration:.07f delay: 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: 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 == || _longPressGesture.state == ) {
return NO;
}
}else if ([_longPressGesture isEqual:gestureRecognizer]) {
if (self.collectionView.panGestureRecognizer.state != && self.collectionView.panGestureRecognizer.state != ) {
return NO;
}
}
return YES;
} - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([_panGesture isEqual:gestureRecognizer]) {
if (_longPressGesture.state != && _longPressGesture.state != ) {
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 == || _longPressGesture.state == ) {
return NO;
}
}
return YES;
} @end
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();
make.size.mas_equalTo(CGSizeMake(, ));
}]; self.imageView = [[UIImageView alloc] init];
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
self.imageView.image = [UIImage imageNamed:@""];
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();
make.size.mas_equalTo(CGSizeMake(, ));
}];
} // 点击加载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(, , 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 = ; i < ; i ++) {
ItemModel * model = [[ItemModel alloc] init];
model.imageName = [NSString stringWithFormat:@"%zd",i];
model.titleName = [NSString stringWithFormat:@"第%zd张",i];
[_dataSource addObject:model];
}
}
return _dataSource;
}
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
#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 = ;
self.backgroundColor = RGB(, , , 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(-);
make.top.equalTo(self.underBackView.mas_top).with.offset();
make.size.mas_equalTo(CGSizeMake(, ));
}]; [self.selectedButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.underBackView);
make.bottom.equalTo(self.underBackView.mas_bottom).with.offset(-);
make.size.mas_equalTo(CGSizeMake(, ));
}]; [self.nameLabel mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self.selectedButton);
make.bottom.equalTo(self.selectedButton.mas_top).with.offset(-);
make.size.mas_equalTo(CGSizeMake(, ));
}];
} #pragma makr - collectionView delegate
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return ;
} - (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 = ;
_underBackView.originY = ;
_underBackView.width = SCREEN_WIDTH - * _underBackView.originX;
_underBackView.height = SCREEN_HEIGHT - * _underBackView.originY;
_underBackView.layer.cornerRadius = ;
_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:];
_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 / , self.underBackView.width - );
layout.minimumLineSpacing = ;
layout.minimumInteritemSpacing = ;
layout.needAlpha = YES;
layout.delegate = self;
CGFloat oneX =self.underBackView.width / ;
layout.sectionInset = UIEdgeInsetsMake(, oneX, , oneX); _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(, , 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:];
[self.collectionView reloadData];
} // 给指定的label赋值
- (void)labelText:(NSInteger)idx{
ItemModel * model = self.dataSource[idx];
self.nameLabel.text = model.titleName;
}
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
#import "FlowLayout7.h"
#import "Constant.h" @implementation FlowLayout7
{
NSInteger _index;
} // 初始化方法
- (instancetype)init{
if (self == [super init]) {
_index = ;
}
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, , 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 / ;
//3. 获取距离中心的的偏移量,需要绝对值
CGFloat absOffset = fabs(halfCenterX - leftMargin);
//4. 获取的实际的缩放比例 距离中心越多,这个值就越小,也就是item的scale越小 中心是方法最大的
CGFloat scale = - absOffset / halfCenterX;
//5. 缩放
attributes.transform3D = CATransform3DMakeScale( + scale * MKJMinZoomScale, + scale * MKJMinZoomScale, );
// 是否需要透明
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 == ){
if (newBounds.origin.x < SCREEN_WIDTH / ){
if (_index != indexPathNow.row){
_index = ;
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 / ;
// 3. 拿到可见区域的rect
CGRect visibleRec = CGRectMake(proposedContentOffset.x, , 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 < ) {
centerOffsetX = ;
} if (centerOffsetX > self.collectionView.contentSize.width -(self.sectionInset.left + self.sectionInset.right + self.itemSize.width)) {
centerOffsetX = floor(centerOffsetX);
}
return CGPointMake(centerOffsetX, proposedContentOffset.y);
}
@end
PopCollectionViewCell : UICollectionViewCell
#import <UIKit/UIKit.h> @interface PopCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UIImageView *heroImageVIew; @end
#import "PopCollectionViewCell.h" @implementation PopCollectionViewCell - (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
} @end
ItemModel : NSObject
#import <Foundation/Foundation.h> @interface ItemModel : NSObject
@property (nonatomic,copy) NSString *imageName;
@property (nonatomic,copy) NSString *titleName;
@end
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; // 最小缩放比例
#import "Constant.h" const CGFloat MKJLineSpacing = .f;
const CGFloat MKJZoomScale = 1.45f;
const CGFloat MKJMinZoomScale = MKJZoomScale - 1.0f;
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
#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/, self.bounds.size.height/);
}
- (CGFloat)centerXOfCurrentView
{
return self.bounds.size.width/;
}
- (CGFloat)centerYOfCurrentView
{
return self.bounds.size.height/;
} - (CGFloat)centerX
{
return (self.frame.origin.x+self.bounds.size.width/);
}
- (CGFloat)centerY
{
return (self.frame.origin.y+self.bounds.size.height/);
} - (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/;
self.frame = frame;
}
- (void)setCenterY:(CGFloat)centerY
{
CGRect frame = self.frame;
frame.origin.y = centerY - self.height/;
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
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:];
self.layout = self.cardLayout;
self.cardLayout.delegate = self;
[self.view addSubview:self.collectionView];
} - (UICollectionView *)collectionView {
if (!_collectionView) {
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(, , self.view.bounds.size.width, collectionHeight) collectionViewLayout:self.layout];
[_collectionView registerClass:[CardCollectionViewCell class] forCellWithReuseIdentifier:@"cardCell"];
_collectionView.delegate = self;
_collectionView.dataSource = self;
[_collectionView setContentOffset:CGPointMake(, )];
_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 = ; row < [self.collectionView numberOfItemsInSection:]; row++) {
CardCollectionViewCell * cell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:]];
CGFloat blur = ((NSNumber *)[((CardLayout *)self.layout).blurList objectAtIndex:row]).floatValue;
[cell setBlur:blur];
}
}else{
for (NSInteger row = ; row < [self.collectionView numberOfItemsInSection:]; row++) {
CardCollectionViewCell * cell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:]];
[cell setBlur:];
}
}
} - (void)updateBlur:(CGFloat) blur ForRow:(NSInteger)row{
if (![self.layout isKindOfClass:[CardLayout class]]) {
return;
}
CardCollectionViewCell * cell = (CardCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:]];
[cell setBlur:blur];
} #pragma mark - UICollectionViewDataSource - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return ;
} - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return ;
} - (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%)];
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:];
}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];
}
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
#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:];
for (NSInteger row = ; row < rowCount; row++) {
[_blurList addObject:@];
}
}
return _blurList;
} - (id)init{
self = [self initWithOffsetY:];
return self;
} - (instancetype)initWithOffsetY:(CGFloat)offsetY{
self = [super init];
if (self) {
cellWidth = [UIScreen mainScreen].bounds.size.width -*;
cellHeight = ;
self.offsetY = offsetY;
self.cellLayoutList = [NSMutableArray array]; self.screenHeight = [UIScreen mainScreen].bounds.size.height;
self.m0 = ;
self.n0 = ;
self.deltaOffsetY = ;
}
return self;
} - (void)prepareLayout{
[super prepareLayout];
[self.cellLayoutList removeAllObjects];
NSInteger rowCount = [self.collectionView numberOfItemsInSection:];
for (NSInteger row = ; row < rowCount; row++) {
UICollectionViewLayoutAttributes* attribute = [self layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:row inSection:]];
[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:];
if (rowCount <= ) {
return self.collectionView.frame.size.height;
}
CGFloat scrollY = self.deltaOffsetY*(rowCount-);
return scrollY + self.screenHeight;
} //目标offset,在应用layout的时候会调用这个回调来设置collectionView的contentOffset
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset{
return CGPointMake(, 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:];
//如果超过两张卡片,则用多卡片布局
if (rowCount > ) {
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) / , centerY); //计算缩放比例
CGFloat rat = [self transformRatio:originY];
attributes.transform = CGAffineTransformMakeScale(rat, rat); //计算透明度
//y = (1-1.14x)^0.3
CGFloat blur = ;
if ((-1.14*rat) < ) {
blur = ;
}else{
blur = powf((-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 = ;
if (tmp >= ) {
y = powf((tmp)/mi, )*ni;
}else{
y = - (cellHeight - tmp);
}
return y;
} //根据偏移量、下标获取对应的尺寸变化
- (CGFloat)transformRatio:(CGFloat)originY{
// y = (x/range)^0.4
if (originY < ) {
return ;
}
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 = ; //初始状态
CGFloat xi = x0 - self.deltaOffsetY*row;
CGFloat ni = powf((self.m0 - xi)/self.m0, )*self.n0;
// NSLog(@"defaultY-%d: %f",(int)row,ni);
return ni;
} - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return !CGRectEqualToRect(newBounds, self.collectionView.bounds);
} @end
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
#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-*;
cellHeight = ;
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(, self.contentOffsetY)];
CGFloat scale = ;
CGFloat width = cellWidth;
CGFloat height = cellHeight;
NSInteger rowsCount = [self.collectionView numberOfItemsInSection:];
for (NSInteger row = ; row < rowsCount; row++) {
NSIndexPath* cellIndexPath = [NSIndexPath indexPathForRow:row inSection:];
UICollectionViewLayoutAttributes* attribute = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:cellIndexPath];
CGFloat centerX = self.collectionView.bounds.size.width/2.0;
CGFloat currentY = ;
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 = ; }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(, 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
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
#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, + 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 = ;
self.layer.masksToBounds = YES;
} - (UILabel *)titleLabel{
if (!_titleLabel) {
_titleLabel = [[UILabel alloc]init];
_titleLabel.font = [UIFont systemFontOfSize:];
_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
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(, CGRectGetMaxY(self.collectionView.frame), self.view.frame.size.width, )];
_indexLabel.textAlignment = NSTextAlignmentCenter;
_indexLabel.font = [UIFont systemFontOfSize:];
_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+,_allCount];
};
layout.itemSize = CGSizeMake(, ); _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(, , self.view.frame.size.width, ) 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;
}
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
#import "FlowLayout9.h" @implementation FlowLayout9
{
CGFloat _viewHeight;
CGFloat _itemHeight;
CGFloat _DefaultInsetLeft;
} /**
* 覆写prepareLayout,储存布局信息
*/
- (void)prepareLayout{
[super prepareLayout];
self.visibleCount = self.visibleCount < ?:self.visibleCount;
_viewHeight = CGRectGetWidth(self.collectionView.frame);
_itemHeight = self.itemSize.width;
//初始状态
_DefaultInsetLeft = _DefaultInsetLeft == ?-(_viewHeight - _itemHeight)/ :_DefaultInsetLeft;
self.collectionView.contentInset = UIEdgeInsetsMake(, _DefaultInsetLeft, , (_viewHeight - _itemHeight) / );
} /**
* 储存视图内容
*
* @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 / ;
CGFloat attributesY = _itemHeight * indexPath.row + _itemHeight / ;
attributes.zIndex = -ABS(attributesY - cY); CGFloat delta = cY - attributesY;
CGFloat ratio = - delta / (_itemHeight * );
CGFloat scale = - 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) / );
return attributes;
} /**
* 返回内容尺寸
*
* @return 内容尺寸
*/
- (CGSize)collectionViewContentSize{
NSInteger cellCount = [self.collectionView numberOfItemsInSection:];
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:];
CGFloat centerY = ( self.collectionView.contentOffset.x) + _viewHeight / ;
NSInteger index = centerY / _itemHeight;
NSInteger count = (self.visibleCount - ) / ;
NSInteger minIndex = MAX(, (index - count));
NSInteger maxIndex = MIN((cellCount - ), (index + count));
NSMutableArray * array = [NSMutableArray array];
for (NSInteger i = minIndex; i <= maxIndex; i++) {
NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:];
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 / - _itemHeight / ) / _itemHeight);
proposedContentOffset.x = _itemHeight * index + _itemHeight / - _viewHeight / ;
if (self.SlideIndexBlock) {
self.SlideIndexBlock((NSInteger)index);
}
_DefaultInsetLeft = (_viewHeight - _itemHeight)/ ;
return proposedContentOffset;
} - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return !CGRectEqualToRect(newBounds, self.collectionView.bounds);
} @end
CarouselViewModel
#import <Foundation/Foundation.h> @interface CarouselViewModel : NSObject
@property (nonatomic, strong) NSMutableArray * data;
- (void)getData;
@end
#import "CarouselViewModel.h"
#import "CarouseModel.h" @implementation CarouselViewModel
- (void)getData{
NSInteger count = ;
NSMutableArray * data = [[NSMutableArray alloc] initWithCapacity:count];
int frakeIndex = ;
for (int i = ; 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 > ? :frakeIndex;
[data addObject:model];
}
self.data = data;
}
@end
CarouselUIService
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class CarouselViewModel; @interface CarouselUIService : NSObject<UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) CarouselViewModel * viewModel;
@end
#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
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
#import "CarouseModel.h" @implementation CarouseModel @end
UICollectionViewCell
#import <UIKit/UIKit.h>
@class CarouseModel; @interface CarouseCollectionViewCell : UICollectionViewCell
@property (nonatomic, strong) CarouseModel * model;
@end
#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
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://*.com/questions/19345864/nsjsonreadingmutableleaves-option-is-not-working
//NSJSONReadingAllowFragments:允许JSON字符串最外层既不是NSArray也不是NSDictionary,但必须是有效的JSON Fragment。例如使用这个选项可以解析 @“123” 这样的字符串。参见:http://*.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 = ;
layout.minimumLineSpacing = ;
_collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(, , self.view.bounds.size.width - , self.view.bounds.size.height - ) 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 - ) / , (ScreenWidth - ) / + );
} else {
return CGSizeMake(ScreenWidth - , (ScreenWidth - ) / + );
}
}
- (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];
}
}
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
#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 = ;
_titleLabel.font = [UIFont boldSystemFontOfSize:];
[self.contentView addSubview:_titleLabel]; _priceLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_priceLabel.textColor = [UIColor redColor];
_priceLabel.font = [UIFont systemFontOfSize:];
[self.contentView addSubview:_priceLabel];
} - (void)setIsGrid:(BOOL)isGrid{
_isGrid = isGrid;
if (isGrid) {
_imageV.frame = CGRectMake(, , self.bounds.size.width - , self.bounds.size.width - );
_titleLabel.frame = CGRectMake(, self.bounds.size.width - , ScreenWidth/, );
_priceLabel.frame = CGRectMake(, self.bounds.size.width - , ScreenWidth/, );
} else {
_imageV.frame = CGRectMake(, , self.bounds.size.height - , self.bounds.size.height - );
_titleLabel.frame = CGRectMake(self.bounds.size.height + , , ScreenWidth/, self.bounds.size.height - );;
_priceLabel.frame = CGRectMake(self.bounds.size.height + , self.bounds.size.height - , ScreenWidth/, );;
}
} - (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
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
#import "GridListModel.h" @implementation GridListModel @end
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
#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 = ; i < count; i++)
{
Ivar ivar = ivars[i]; // 取出的成员变量,去掉下划线
NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
NSString *key = [ivarName substringFromIndex:]; 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
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*/, [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, ), ^{
for (NSArray * item in mxData){
@try {
MXModel * model = [[MXModel alloc] init];
model.title = item[];
model.snap = [UIImage imageNamed:item[]];
model.icon = [UIImage imageNamed:item[]];
[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 ;
}
- (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];
}
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
#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 = ;
_snapView.clipsToBounds = YES;
_appIcon.layer.cornerRadius = ;
_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 == ){
self.appNameLabel.alpha = ;
}else{
self.appNameLabel.alpha = self.alpha / ;
}
}];
} - (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]){
self.backgroundColor = [UIColor clearColor];
_snapView.layer.cornerRadius = ;
_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
UICollectionViewLayout
#import <UIKit/UIKit.h> @interface FMLayout : UICollectionViewLayout
@property (nonatomic, assign) CGSize itemSize;
@end
#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 < ){
self.visibleCount = ;
}
self.viewWidth = CGRectGetWidth(self.collectionView.frame);
self.virtualItemWidth = self.itemSize.width/;
} - (CGSize)collectionViewContentSize {
NSInteger cellCount = [self.collectionView numberOfItemsInSection:];
return CGSizeMake(cellCount * _virtualItemWidth + MARGIN * , [UIScreen mainScreen].bounds.size.height);
} - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect{
NSInteger cellCount = [self.collectionView numberOfItemsInSection:];
CGFloat centerX = (self.collectionView.contentOffset.x) + _viewWidth / ;
NSInteger index = centerX / _virtualItemWidth;
NSInteger count = (self.visibleCount - ) / ;
NSInteger minIndex = MAX(, (index - count - ));
NSInteger maxIndex = MIN((cellCount - ), (index + count));
NSMutableArray * array = [NSMutableArray array];
for (NSInteger i = minIndex; i <= maxIndex; i++) {
NSIndexPath * indexPath = [NSIndexPath indexPathForItem:i inSection:];
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 / ;
CGFloat attributesX = _virtualItemWidth * indexPath.row + _virtualItemWidth / + MARGIN;
attributes.zIndex = attributesX; CGFloat delta = cX - attributesX; CGFloat ratio = delta / (_virtualItemWidth); CGFloat centerX = attributesX; CGFloat offset = pow((-ratio + ), )/ * _itemSize.width;
centerX = cX - _viewWidth/ + offset + _itemSize.width / - ; CGFloat scale = (-ratio + - )/ + ;
attributes.transform = CGAffineTransformMakeScale(scale, scale);//x scale has something doesn`t right if (ratio > 1.3){
attributes.alpha = MAX(, 2.3 - ratio);
} attributes.center = CGPointMake(centerX, CGRectGetHeight(self.collectionView.frame) / );
return attributes;
} - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
CGFloat index = roundf((proposedContentOffset.x + _viewWidth / - _itemSize.width / ) / _virtualItemWidth);
proposedContentOffset.x = _virtualItemWidth * index + _itemSize.width / - _viewWidth / ;
return proposedContentOffset;
} - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
return !CGRectEqualToRect(newBounds, self.collectionView.bounds);
} @end
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
#import "MXModel.h" @implementation MXModel @end
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 */
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
#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
UIImage+MXAdd
#import <UIKit/UIKit.h> @interface UIImage (MXAdd) + (UIImage *)resizeImage:(UIImage*)image toViewSize:(CGSize)size;
+ (UIImage *)simpleResizeImage:(UIImage*)image toSize:(CGSize)size; @end
#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(, , 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) / * 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) / * 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={,,size};
UIGraphicsBeginImageContextWithOptions(size, NO, );
[image drawInRect:rect];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
} @end