本篇一起来学习如何使用uicollectionview来实现水平滚动的缩放式卡片布局,就像nice app中的卡片布局。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIml2Zu8WblRWZsF2YzRmchN2LcNDMvwlNxAjMvw1ckF2bsBXdvwFduVGdu92YtA3dvwVbvNmLvVHazlmblhmL3d3dvw1LcpDc0RHaiojIsJye.gif)
从demo效果图中,可以看出来,主要是缩放系数的计算。对于不同距离的cell,其缩放系数要变化,以便整体协调显示。
所以,我们必须重写-layoutattributesforelementsinrect:方法来实现所有当前可见的cell的attributes。
计算比例,通过获取当前偏移rect的最小坐标x,再与atribute的中心x相减,再除以高度,就是高度的缩放倍数scalefordistance。
最后,通过一个公式来计算缩放的y系数。公式为:
1
2
3
scale = 1 + scalefactor * (1 - fabs(scalefordistance))
scalefactor因子可以自由调整,值越大,显示就越大。
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
#pragma mark - override
- (void)preparelayout {
self.scrolldirection = uicollectionviewscrolldirectionhorizontal;
self.minimumlinespacing = 20;
// self.minimuminteritemspacing = 20;
self.sectioninset = uiedgeinsetsmake(0, 30, 0, 30);
self.itemsize = cgsizemake(self.collectionview.frame.size.width - 60,
self.collectionview.frame.size.height - 180);
[super preparelayout];
}
- (bool)shouldinvalidatelayoutforboundschange:(cgrect)newbounds {
return yes;
- (nsarray<uicollectionviewlayoutattributes *> *)layoutattributesforelementsinrect:(cgrect)rect {
nsarray *superattributes = [super layoutattributesforelementsinrect:rect];
nsarray *attributes = [[nsarray alloc] initwitharray:superattributes copyitems:yes];
cgrect visiblerect = cgrectmake(self.collectionview.contentoffset.x,
self.collectionview.contentoffset.y,
self.collectionview.frame.size.width,
self.collectionview.frame.size.height);
cgfloat offset = cgrectgetmidx(visiblerect);
[attributes enumerateobjectsusingblock:^(uicollectionviewlayoutattributes *attribute, nsuinteger idx, bool * _nonnull stop) {
cgfloat distance = offset - attribute.center.x;
// 越往中心移动,值越小,那么缩放就越小,从而显示就越大
// 同样,超过中心后,越往左、右走,缩放就越大,显示就越小
cgfloat scalefordistance = distance / self.itemsize.height;
// 0.2可调整,值越大,显示就越大
cgfloat scaleforcell = 1 + 0.2 * (1 - fabs(scalefordistance));
// only scale y-axis
attribute.transform3d = catransform3dmakescale(1, scaleforcell, 1);
attribute.zindex = 1;
}];
return attributes;
- (cgpoint)targetcontentoffsetforproposedcontentoffset:(cgpoint)proposedcontentoffset withscrollingvelocity:(cgpoint)velocity {
bool pagingenabled = no;
if (pagingenabled) {
// 分页以1/3处
if (proposedcontentoffset.x > self.previousoffsetx + self.itemsize.width / 3.0) {
self.previousoffsetx += self.collectionview.frame.size.width - self.minimumlinespacing * 2;
} else if (proposedcontentoffset.x < self.previousoffsetx - self.itemsize.width / 3.0) {
self.previousoffsetx -= self.collectionview.frame.size.width - self.minimumlinespacing * 2;
}
proposedcontentoffset.x = self.previousoffsetx;
} else {
cgfloat x = proposedcontentoffset.x / (self.itemsize.width + self.minimumlinespacing);
int base = (int)x;
proposedcontentoffset.x = base * (self.itemsize.width + self.minimumlinespacing);
}
return proposedcontentoffset;
这里是以1/3.0为分界,左、右的1/3作为分界线,超过才会切换过去!
感谢评论的朋友们的一句话,点醒了笔者。对于不分页的情况下,其实只要使用当前的偏移x除(itemsize.width + minimumlinespacing)就得到一个倍数,然后四舍五入。比如,4.3取整得到4,那么就是没有超过一半,就要往回滚。而4.6取整得到5,表示要滚动到下一个。所以在不分页的情况下,其实也是非常简单的。
本篇文章经过朋友们的评论及反馈,可以说已经完善了!