TXCarouselViewLayout.m 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. //
  2. // TXCarouselViewLayout.m
  3. // HJCarouselDemo
  4. //
  5. // Created by 新华龙mac on 2018/1/17.
  6. // Copyright © 2018年 新华龙mac. All rights reserved.
  7. //
  8. #import "TXCarouselViewLayout.h"
  9. #define INTERSPACEPARAM 0.90
  10. @interface TXCarouselViewLayout ()
  11. @property (nonatomic, assign) CGFloat collectionViewWidth;
  12. @property (nonatomic, assign) CGFloat itemWidth;
  13. @property (nonatomic, assign) CGFloat lastDirectionIndex;
  14. @property (nonatomic, assign) CGFloat slidDistance;
  15. @property (nonatomic, strong) NSIndexPath *lastIndexOne;
  16. @end
  17. @implementation TXCarouselViewLayout
  18. /**
  19. * 当collectionView的显示范围发生改变的时候,是否需要重新刷新布局
  20. * 一旦重新刷新布局,就会重新调用下面的方法
  21. 1.prepareLayout
  22. 2.layoutAttributesForElementsInRect:方法
  23. */
  24. - (void)prepareLayout{
  25. [super prepareLayout];
  26. self.visibleCount = 9;
  27. self.collectionViewWidth = CGRectGetWidth(self.collectionView.frame);
  28. self.itemWidth = self.itemSize.width;
  29. if (self.itemSize.width>500){//
  30. // self.itemWidth = self.itemSize.width/1.75;
  31. }
  32. }
  33. - (CGSize)collectionViewContentSize{
  34. NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
  35. if (self.scrollDirection == UICollectionViewScrollDirectionVertical){
  36. return CGSizeMake(CGRectGetWidth(self.collectionView.frame), cellCount * self.itemWidth);
  37. }
  38. return CGSizeMake(cellCount * self.itemWidth, CGRectGetHeight(self.collectionView.frame));
  39. }
  40. /**
  41. * 这个方法的返回值是一个数组(数组里面存放着rect范围内所有元素的布局属性)
  42. * 这个数组中存放的都是UICollectionViewLayoutAttributes对象
  43. * 这个方法的返回值决定了rect范围内所有元素的排布(frame)
  44. * */
  45. - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect{
  46. NSInteger cellCount = [self.collectionView numberOfItemsInSection:0];
  47. CGFloat centerX = self.collectionView.contentOffset.x + self.collectionViewWidth / 2;
  48. NSInteger totalCount = centerX / self.itemWidth;
  49. NSInteger count = (self.visibleCount) / 2;
  50. NSInteger minIndex = MAX(0, (totalCount - count));
  51. NSInteger maxIndex = MIN((cellCount - 1), (totalCount + count));
  52. // NSLog(@"minIndex = %ld maxIndex = %ld",minIndex,maxIndex);
  53. NSMutableArray *array = [NSMutableArray array];
  54. for (NSInteger i = minIndex; i <= maxIndex; i++){
  55. NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
  56. UICollectionViewLayoutAttributes *attributes = [self layoutAttributesForItemAtIndexPath:indexPath];
  57. [array addObject:attributes];
  58. }
  59. return array;
  60. //
  61. }
  62. //返回每个cell的中心值和 缩放系数
  63. - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath{
  64. UICollectionViewLayoutAttributes *attributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
  65. attributes.size = self.itemSize;
  66. CGFloat cX = self.collectionView.contentOffset.x + self.collectionViewWidth / 2;
  67. CGFloat attributesX = self.itemWidth * indexPath.row + self.itemWidth / 2;//
  68. attributes.zIndex = -ABS(attributesX - cX);
  69. CGFloat delta = cX - attributesX;
  70. CGFloat ratio = - delta / (self.itemWidth * 2);
  71. CGFloat scale = 0.6 - ABS(delta) / (self.itemWidth * 6.0) * cos(ratio * M_2_PI*0.9);
  72. attributes.transform = CGAffineTransformMakeScale(scale, scale);
  73. if (self.itemSize.width>500){//
  74. // attributes.transform = CGAffineTransformMakeScale(scale/1.75, scale/1.75);
  75. }
  76. CGFloat centerY = attributesX;
  77. // NSLog(@"scale= %f",scale);
  78. // NSLog(@"indexPath = %ld",(long)indexPath.row);
  79. // NSLog(@"self.lastIndexOne = %ld",(long)self.lastIndexOne.row);
  80. // NSLog(@"scale ======== %f",scale);
  81. if (scale>0.999){//滑动到最前面时, 交换持有者
  82. self.lastIndexOne = indexPath;
  83. }
  84. if (self.lastIndexOne == indexPath){
  85. CGFloat index1 = 0.0f;
  86. if ([self judgeDirection:centerY]){
  87. index1 = 2;
  88. }else{
  89. index1 = -2;
  90. }
  91. centerY = cX + sin(ratio * 1.31) * self.itemWidth * INTERSPACEPARAM*2.2+index1;
  92. //双保险,如果滑动急快的情况下,方法没有获取到最高点的坐标会导致判断失败scale>0.999 实效,
  93. //所以启用判断scale <= 0.84加以纠正。(且只有在上述方法实效的情况下调用,所以不能使用|| ,不然有冲突,)
  94. if (scale <= 0.84){
  95. if ([self judgeDirection:centerY]){
  96. NSIndexPath *indexNext = [NSIndexPath indexPathForRow:self.lastIndexOne.row+1 inSection:0];
  97. self.lastIndexOne = indexNext;
  98. }else{
  99. NSIndexPath *indexNext = [NSIndexPath indexPathForRow:self.lastIndexOne.row-1 inSection:0];
  100. self.lastIndexOne = indexNext;
  101. }
  102. }
  103. if (scale <=0.9172) {
  104. CGFloat sinIndex = sin(ratio * 1.31) * self.itemWidth * INTERSPACEPARAM*2.7+index1;
  105. if ([self judgeDirection:centerY]) {
  106. centerY = centerY-(sinIndex+(self.itemSize.width*96/124));
  107. }else{
  108. centerY = centerY-(sinIndex-(self.itemSize.width*96/124));
  109. }
  110. }
  111. self.lastDirectionIndex = centerY;
  112. }else{
  113. centerY = cX + sin(ratio * 1.217) * self.itemWidth * INTERSPACEPARAM;
  114. }
  115. attributes.center = CGPointMake(centerY, CGRectGetHeight(self.collectionView.frame) / 2);
  116. return attributes;
  117. }
  118. /**
  119. * 这个方法的返回值,就决定了collectionView停止滚动时的偏移量
  120. * 这个方法在滚动松手的那一刻调用
  121. */
  122. - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity{
  123. // NSLog(@"proposedContentOffset = (%f,%f)",proposedContentOffset.x,proposedContentOffset.y);
  124. // NSLog(@"velocity = (%f,%f)",velocity.x,velocity.y);
  125. CGFloat index = roundf((proposedContentOffset.x + self.collectionViewWidth / 2 - self.itemWidth / 2) / self.itemWidth);
  126. // NSLog(@"test1 = %f",proposedContentOffset.x + self.collectionViewWidth / 2 - self.itemWidth / 2);
  127. // NSLog(@"test2 = %f",(proposedContentOffset.x + self.collectionViewWidth / 2 - self.itemWidth / 2) / self.itemWidth);
  128. // [index intVelue];
  129. // NSLog(@"self.collectionViewWidth = %f",self.collectionViewWidth);
  130. // NSLog(@"self.itemWidth = %f",self.itemWidth);
  131. // NSLog(@"indexindexindexindex = %f",index);
  132. proposedContentOffset.x = self.itemWidth * index + self.itemWidth / 2 - self.collectionViewWidth / 2;
  133. // proposedContentOffset 预计的位置(滚动结束时)
  134. NSInteger i = (NSInteger)index;
  135. [[NSNotificationCenter defaultCenter] postNotificationName:@"ThisIsANoticafication" object:@{@"parameter":@(i)}];
  136. // //leon 回调i值
  137. // NSInteger i = (NSInteger)index;
  138. // if (self.TargetScrollEnd){
  139. // self.TargetScrollEnd(i);
  140. // }
  141. return proposedContentOffset;
  142. }
  143. //判断滑动的方向(yes往左,no为右);
  144. -(BOOL)judgeDirection:(CGFloat)index{
  145. if (self.lastDirectionIndex>index){
  146. NSLog(@"滑动 = YES");
  147. return YES;
  148. }else{
  149. NSLog(@"滑动 = NO");
  150. return NO;
  151. }
  152. }
  153. - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds{
  154. return YES;
  155. }
  156. @end