wxr
2020-06-15 b8e94316e41eba72d927d5ca7d931b26139ee8ff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//
//  HDLCoverFlowLayout.m
//  CoverFlow
//
//  Created by JLCHEN on 2019/8/26.
//  Copyright © 2019 JLCHEN. All rights reserved.
//
 
#import "HDLCoverFlowLayout.h"
 
 
@implementation HDLCoverFlowLayout
 
- (void)prepareLayout {
    
    [super prepareLayout];
    
    // 1.调整滚动方向
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    
    // 2.设置大小
    CGFloat itemH = self.collectionView.bounds.size.height * 0.8;
    CGFloat itemW = self.collectionView.bounds.size.width * 0.65;
    
    self.itemSize = CGSizeMake(itemW, itemH);
    
    // 3.设置间距
    self.minimumLineSpacing = 0;
    
//    self.minimumInteritemSpacing  = 0;
    
    // 4.设置内边距
    CGFloat inset = (self.collectionView.bounds.size.width - itemW) * 0.5;
//     CGFloat inset = 0;
    self.sectionInset = UIEdgeInsetsMake(0, inset, 0, inset);
    
    
    
}
 
#pragma mark - 布局一定却与的cell
- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    
    // 1.先获取系统布局好的结果!
    NSArray<UICollectionViewLayoutAttributes *> *oldAttrsArr = [super layoutAttributesForElementsInRect:rect];
    
    // 2.遍历集合,进行修改
    // 2.1 临时集合
    NSMutableArray *tempArrM = [NSMutableArray array];
    
    // 2.2 遍历集合,修改属性
    [oldAttrsArr enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        // 2.2.1 修改之前先copy!
        UICollectionViewLayoutAttributes *newAttr = obj.copy;
        
        //        newAttr.transform3D = CATransform3DRotate(newAttr.transform3D, M_PI_4, 0, 1, 0);
        // 2.2.2 修改属性! -> 缩放 & 旋转!
        // MARK: - 1.缩放
        // - 1.屏幕中线的位置
        CGFloat screenCenterX = self.collectionView.bounds.size.width * 0.5 + self.collectionView.contentOffset.x;
        
        // - 2.每个cell的中心的x!!
        CGFloat itemCenterX = newAttr.center.x;
        
        // - 3.计算距离
        CGFloat distance = screenCenterX - itemCenterX;
        
        // - 4.将距离转换成缩放的比例! ABS() -> 取绝对值!
        CGFloat scale = 1 - ABS(distance/4) / self.collectionView.bounds.size.width;
        
//        NSLog(@"scale: %f",scale);
        
        // - 5.旋转
        // - 5.1 旋转的角度!
        CGFloat angle = (1 - scale) * M_PI_2 *2;
        
        // 如果距离大于0,左边 -> * 1,正的角度
        // 如果距离小于0,右边 -> * -1,负的角度!
        angle *= ((distance > 0) ? 1 : -1);
        
        // - 单位矩阵
        CATransform3D transform = CATransform3DIdentity;
        
        // - 5.3 增加透视效果
        transform.m34 = - 1.0 / 500;
        
        
        // - 4.2 缩放
        transform = CATransform3DScale(transform, scale, scale, 1);
        
        // - 5.2 旋转
        transform = CATransform3DRotate(transform, angle, 0, 1, 0);
        
        
        // 4.3 赋值!
        newAttr.transform3D = transform;
        
        // 2.2.x 保存到临时集合
        [tempArrM addObject:newAttr];
    }];
    
    // 3.返回
    return tempArrM;
    
}
 
#pragma mark - 只要显示的区域发生变化,就重新计算布局!
// Invalidate 失效! 返回YES! 只要显示的区域发生改变,就让布局失效!
// 重新计算布局!
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
    
    return YES;
}
 
#pragma mark - 计算停留的位置
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
    
    // 1.获取系统计算好的值
    CGPoint point = [super targetContentOffsetForProposedContentOffset:proposedContentOffset withScrollingVelocity:velocity];
    
    // 2.计算屏幕可见区域 -> CGRect!
    CGRect rect;
    rect.size = self.collectionView.bounds.size;
#warning - 预计停留的位置时,内容的偏移量!
    rect.origin = proposedContentOffset;
    
    // 3.获取可见区域内的cell!
#warning - 利用上面的方法,计算可见区域内的cell!
    NSArray<UICollectionViewLayoutAttributes *> *visibleAttrs = [self layoutAttributesForElementsInRect:rect];
    
    // 4.需要找出其中最近的cell!
    // 4.2 遍历,找出距离屏幕中线最近的cell对应的attr
    // - 屏幕中线的x
    CGFloat screenCenterX = proposedContentOffset.x + self.collectionView.bounds.size.width * 0.5;
    
    // - 定义一个最小的间距
    __block CGFloat minMargin = CGFLOAT_MAX;
    // - 定义一个索引,用来记录最近那个attr的在数组中!索引!
    __block NSInteger index = -1;
    
    [visibleAttrs enumerateObjectsUsingBlock:^(UICollectionViewLayoutAttributes * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        CGFloat distance = screenCenterX - obj.center.x;
        
        if (ABS(distance) < minMargin) {
            minMargin = ABS(distance);
            index = idx;
        }
        
    }];
    
    // 5.计算需要偏移的距离!
    CGFloat offsetX = screenCenterX - visibleAttrs[index].center.x;
    
    // 2.返回点坐标
    return CGPointMake(point.x - offsetX, point.y);
}
 
 
 
 
@end