uicollectionview真的好強大,今天我們來研究一下這種很常見的卡片動畫效果是如何實作了。本篇不能太深入地講解,因為筆者也是剛剛摸索出點眉目,但是并沒有深刻地了解。如果在講解過程中,出現不對的地方,請及時回報。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiZpdmLv1WZkRmchN2LcNDMvwlNxAjMvw1ckF2bsBXdvwFduVGdu92YtA3dvwVbvNmLvVHazlmblhmL3d3dvw1LcpDc0RHaiojIsJye.gif)
1
2
3
4
5
6
7
8
9
10
11
12
// 我們必須重寫此方法,指定布局大小
// 每次layout invalidated或者重新query布局資訊時,會調用
- (void)preparelayout;
// 用于決定布局資訊
// 我們必須重寫它來實作布局資訊
- (nullable nsarray<__kindof uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect;
// 重寫它來布局資訊
- (nullable uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath;
還有一個非常關鍵的api,必須重寫:
// return yes to cause the collection view to requery the layout for geometry information
// 當重新查詢布局資訊時,就會調用此api。要設定為yes,才能實作自定義布局。
- (bool)shouldinvalidatelayoutforboundschange:(cgrect)newbounds;
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
//
// hybcardflowlayout.m
// collectionviewdemos
// created by huangyibiao on 16/3/26.
// copyright © 2016年 huangyibiao. all rights reserved.
#import "hybcardflowlayout.h"
@interface hybcardflowlayout ()
@property (nonatomic, strong) nsindexpath *mainindexpath;
@property (nonatomic, strong) nsindexpath *willmovetomainindexpath;
@end
@implementation hybcardflowlayout
- (void)preparelayout {
cgfloat inset = 32;
self.itemsize = cgsizemake(self.collectionview.frame.size.width - 2 * inset,
self.collectionview.frame.size.height * 3 / 4);
self.sectioninset = uiedgeinsetsmake(0, inset, 0, inset);
self.scrolldirection = uicollectionviewscrolldirectionhorizontal;
[super preparelayout];
}
#pragma mark - override
- (bool)shouldinvalidatelayoutforboundschange:(cgrect)newbounds {
return yes;
- (uicollectionviewlayoutattributes *)layoutattributesforitematindexpath:(nsindexpath *)indexpath {
uicollectionviewlayoutattributes *attribute = [super layoutattributesforitematindexpath:indexpath];
[self settransformforlayoutattributes:attribute];
return attribute;
- (nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect {
nsarray *attributessuper = [super layoutattributesforelementsinrect:rect];
// 一定要深複制一份,不能修改父類的屬性,不然會有很多error列印出來
nsarray *attributes = [[nsarray alloc] initwitharray:attributessuper copyitems:yes];
nsarray *visibleindexpaths = [self.collectionview indexpathsforvisibleitems];
if (visibleindexpaths.count <= 0) {
return attributes;
} else if (visibleindexpaths.count == 1) {
self.mainindexpath = [visibleindexpaths firstobject];
self.willmovetomainindexpath = nil;
} else if (visibleindexpaths.count == 2) {
nsindexpath *indexpath = [visibleindexpaths firstobject];
// 說明是往左滾動
if (indexpath == self.mainindexpath) {
// 記錄将要移進來的位置
self.willmovetomainindexpath = visibleindexpaths[1];
} else {// 往右滾動
self.willmovetomainindexpath = visibleindexpaths[0];
// 更新下一個成為main
self.mainindexpath = visibleindexpaths[1];
}
}
for (uicollectionviewlayoutattributes *attribute in attributes) {
[self settransformforlayoutattributes:attribute];
return attributes;
- (void)settransformforlayoutattributes:(uicollectionviewlayoutattributes *)attribute {
uicollectionviewcell *cell = [self.collectionview cellforitematindexpath:attribute.indexpath];
if (self.mainindexpath && attribute.indexpath.section == self.mainindexpath.section) {
attribute.transform3d = [self tranformforview:cell];
} else if (self.willmovetomainindexpath && attribute.indexpath.section == self.willmovetomainindexpath.section) {
- (catransform3d)tranformforview:(uicollectionviewcell *)view {
// cell的起始偏移
cgfloat w = self.collectionview.frame.size.width;
cgfloat offset = [self.collectionview indexpathforcell:view].section * w;
// 目前偏移
cgfloat currentoffset = self.collectionview.contentoffset.x;
// 計算偏移angle
cgfloat angle = (currentoffset - offset) / w;
catransform3d t = catransform3didentity;
t.m34 = 1.0 / -500;
if (currentoffset - offset >= 0) {
t = catransform3drotate(t, angle, 1, 1, 0);
} else {
t = catransform3drotate(t, angle, -1, 1, 0);
return t;
這裡主要是要處理旋轉。然後要處理切換cell的attribute設定。mainindexpath屬性用于記錄目前顯示的cell的位置。willmovetomainindexpath記錄将要出現的cell的位置。
這裡在慢慢切換時,效果是挺好的,但是如果快速切換卡片,你會發現會有一點點不好之處,就是下一個cell突然出現的。