FriendsListViewLayout.m 7.0 KB

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