TXCarouselViewLayout.m 7.0 KB

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