// // TXCarouselViewLayout.m // HJCarouselDemo // // Created by 新华龙mac on 2018/1/17. // Copyright © 2018年 新华龙mac. All rights reserved. // #import "TXCarouselViewLayout.h" #define INTERSPACEPARAM 0.90 @interface TXCarouselViewLayout () @property (nonatomic, assign) CGFloat collectionViewWidth; @property (nonatomic, assign) CGFloat itemWidth; @property (nonatomic, assign) CGFloat lastDirectionIndex; @property (nonatomic, assign) CGFloat slidDistance; @property (nonatomic, strong) NSIndexPath *lastIndexOne; @end @implementation TXCarouselViewLayout /** * 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局 * 一旦重新刷新布局,就会重新调用下面的方法 1.prepareLayout 2.layoutAttributesForElementsInRect:方法 */ - (void)prepareLayout{ [super prepareLayout]; self.visibleCount = 9; self.collectionViewWidth = CGRectGetWidth(self.collectionView.frame); self.itemWidth = self.itemSize.width; // self.itemWidth = self.itemSize.width; } - (CGSize)collectionViewContentSize{ NSInteger cellCount = [self.collectionView numberOfItemsInSection:0]; if (self.scrollDirection == UICollectionViewScrollDirectionVertical){ return CGSizeMake(CGRectGetWidth(self.collectionView.frame), cellCount * self.itemWidth); } return CGSizeMake(cellCount * self.itemWidth, CGRectGetHeight(self.collectionView.frame)); } /** * 这个方法的返回值是一个数组(数组里面存放着rect范围内所有元素的布局属性) * 这个数组中存放的都是UICollectionViewLayoutAttributes对象 * 这个方法的返回值决定了rect范围内所有元素的排布(frame) * */ - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{ NSInteger cellCount = [self.collectionView numberOfItemsInSection:0]; CGFloat centerX = self.collectionView.contentOffset.x + self.collectionViewWidth / 2; NSInteger totalCount = centerX / self.itemWidth; NSInteger count = (self.visibleCount) / 2; NSInteger minIndex = MAX(0, (totalCount - count)); NSInteger maxIndex = MIN((cellCount - 1), (totalCount + count)); // NSLog(@"minIndex = %ld maxIndex = %ld",minIndex,maxIndex); NSMutableArray *array = [NSMutableArray array]; for (NSInteger i = minIndex; i <= maxIndex; i++){ NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0]; UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath]; [array addObject:attributes]; } return array; // } //返回每个cell的中心值和 缩放系数 - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{ UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath]; attributes.size = self.itemSize; CGFloat cX = self.collectionView.contentOffset.x + self.collectionViewWidth / 2; CGFloat attributesX = self.itemWidth * indexPath.row + self.itemWidth / 2;// attributes.zIndex = -ABS(attributesX - cX); CGFloat delta = cX - attributesX; CGFloat ratio = - delta / (self.itemWidth * 2); CGFloat scale = 0.6 - ABS(delta) / (self.itemWidth * 6.0) * cos(ratio * M_2_PI*0.9); attributes.transform = CGAffineTransformMakeScale(scale, scale); CGFloat centerY = attributesX; // NSLog(@"scale= %f",scale); // NSLog(@"indexPath = %ld",(long)indexPath.row); // NSLog(@"self.lastIndexOne = %ld",(long)self.lastIndexOne.row); // NSLog(@"scale ======== %f",scale); if (scale>0.999){//滑动到最前面时, 交换持有者 self.lastIndexOne = indexPath; } if (self.lastIndexOne == indexPath){ CGFloat index1 = 0.0f; if ([self judgeDirection:centerY]){ index1 = 2; }else{ index1 = -2; } centerY = cX + sin(ratio * 1.31) * self.itemWidth * INTERSPACEPARAM*2.2+index1; //双保险,如果滑动急快的情况下,方法没有获取到最高点的坐标会导致判断失败scale>0.999 实效, //所以启用判断scale <= 0.84加以纠正。(且只有在上述方法实效的情况下调用,所以不能使用|| ,不然有冲突,) if (scale <= 0.84){ if ([self judgeDirection:centerY]){ NSIndexPath *indexNext = [NSIndexPath indexPathForRow:self.lastIndexOne.row+1 inSection:0]; self.lastIndexOne = indexNext; }else{ NSIndexPath *indexNext = [NSIndexPath indexPathForRow:self.lastIndexOne.row-1 inSection:0]; self.lastIndexOne = indexNext; } } if (scale <=0.9172) { CGFloat sinIndex = sin(ratio * 1.31) * self.itemWidth * INTERSPACEPARAM*2.7+index1; if ([self judgeDirection:centerY]) { centerY = centerY-(sinIndex+(self.itemSize.width*96/124)); }else{ centerY = centerY-(sinIndex-(self.itemSize.width*96/124)); } } self.lastDirectionIndex = centerY; }else{ centerY = cX + sin(ratio * 1.217) * self.itemWidth * INTERSPACEPARAM; } attributes.center = CGPointMake(centerY, CGRectGetHeight(self.collectionView.frame) / 2); return attributes; } /** * 这个方法的返回值,就决定了collectionView停止滚动时的偏移量 * 这个方法在滚动松手的那一刻调用 */ - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{ // NSLog(@"proposedContentOffset = (%f,%f)",proposedContentOffset.x,proposedContentOffset.y); // NSLog(@"velocity = (%f,%f)",velocity.x,velocity.y); CGFloat index = roundf((proposedContentOffset.x + self.collectionViewWidth / 2 - self.itemWidth / 2) / self.itemWidth); // NSLog(@"test1 = %f",proposedContentOffset.x + self.collectionViewWidth / 2 - self.itemWidth / 2); // NSLog(@"test2 = %f",(proposedContentOffset.x + self.collectionViewWidth / 2 - self.itemWidth / 2) / self.itemWidth); // [index intVelue]; // NSLog(@"self.collectionViewWidth = %f",self.collectionViewWidth); // NSLog(@"self.itemWidth = %f",self.itemWidth); // NSLog(@"indexindexindexindex = %f",index); proposedContentOffset.x = self.itemWidth * index + self.itemWidth / 2 - self.collectionViewWidth / 2; // proposedContentOffset 预计的位置(滚动结束时) NSInteger i = (NSInteger)index; [[NSNotificationCenter defaultCenter] postNotificationName:@"ThisIsANoticafication" object:@{@"parameter":@(i)}]; // //leon 回调i值 // NSInteger i = (NSInteger)index; // if (self.TargetScrollEnd){ // self.TargetScrollEnd(i); // } return proposedContentOffset; } //判断滑动的方向(yes往左,no为右); -(BOOL)judgeDirection:(CGFloat)index{ if (self.lastDirectionIndex>index){ NSLog(@"滑动 = YES"); return YES; }else{ NSLog(@"滑动 = NO"); return NO; } } - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{ return YES; } @end