CollectionView旋转水平卡片布局
概述
UICollectionView真的好强大,今天我们来研究一下这种很常见的卡片动画效果是如何实现了。本篇不能太深入地讲解,因为笔者也是刚刚摸索出点眉目,但是并没有深刻地理解。如果在讲解过程中,出现不对的地方,请及时反馈。
效果图

重写API
|
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,必须重写:
|
1
2
3
4
5
|
// return YES to cause the collection view to requery the layout for geometry information
// 当重新查询布局信息时,就会调用此API。要设置为YES,才能实现自定义布局。
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds;
|
自定义布局
|
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
|
//
// 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) {
attribute.transform3D = [self tranformForView:cell];
}
}
- (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;
}
@end
|
这里主要是要处理旋转。然后要处理切换cell的attribute设置。mainIndexPath属性用于记录当前显示的cell的位置。willMoveToMainIndexPath记录将要出现的cell的位置。
结尾
这里在慢慢切换时,效果是挺好的,但是如果快速切换卡片,你会发现会有一点点不好之处,就是下一个cell突然出现的。
CollectionView旋转水平卡片布局的更多相关文章
- CollectionView缩放水平卡片布局
实现效果 实现思路 从Demo效果图中,可以看出来,主要是缩放系数的计算.对于不同距离的cell,其缩放系数要变化,以便整体协调显示. 所以,我们必须重写-layoutAttributesForEle ...
- CollectionView垂直缩放卡片布局
实现效果 实现思路 从效果图可以看到变化是,越是往中间滚动的item显示最大,越显眼.而越是往前面,或者越是后面的,反而显示越小,这样就形成了视觉差. 实现的思路就是通过重写在可见范围内的所有item ...
- Java基础之创建窗口——使用卡片布局管理器(TryCardLayout)
控制台程序. 卡片布局管理器会生成一叠组件——一个组件放在另一个组件的上面.添加到容器中的第一个组件在堆栈的顶部,因此是可见的,添加的最后一个组件在堆栈的底部.使用默认的构造函数CardLayout( ...
- 转:三十二、Java图形化界面设计——布局管理器之CardLayout(卡片布局)
转:http://blog.csdn.net/liujun13579/article/details/7773945 卡片布局能够让多个组件共享同一个显示空间,共享空间的组件之间的关系就像一叠牌,组件 ...
- 三十二、Java图形化界面设计——布局管理器之CardLayout(卡片布局)
摘自 http://blog.csdn.net/liujun13579/article/details/7773945 三十二.Java图形化界面设计--布局管理器之CardLayout(卡片布局) ...
- 布局管理器之CardLayout(卡片布局管理器)
对于选项卡这个概念大家可能不会陌生,就是在一个窗口中可以切换显示多页不同的内容,但同一时间只能是其中的某一页可见的,这样的一个个的页面就是选项卡. CardLayout就是类似的这样一个布局管理器,它 ...
- 技术胖Flutter第三季-18布局CardWidget 卡片布局组件
技术胖Flutter第三季-18布局CardWidget 卡片布局组件 博客地址: https://jspang.com/post/flutter3.html#toc-420 最外面是Card布局,里 ...
- 慕课网5-2编程练习:flex布局制作卡片布局案例
慕课网5-2编程练习:flex布局制作卡片布局案例 小伙伴们,学习了卡片布局,接下来我们根据效果图,也写出一个卡片布局的页面吧! 效果图如下: 任务 1.主体内容的卡片一行只能显示两个. 2.卡片与卡 ...
- 设备旋转,创建水平模式布局--Android studio
1.在项目工具窗口中,右键单击res目录后选择new--Android resource directory菜单项. 2.从资源类型Resource type列表中选择layout,保持Source ...
随机推荐
- Jmeter接口测试实战-Cookies
场景: 接口测试时常都需要登录,请求方式(post), 登录常用的方法有通过获取token, 获取session, 获取cookie, 等等. 这几种都有一个共同的特点, 有效期(expires). ...
- python 中range和xrange的区别
range() 相当于直接构造一个列表,而xrange() 是返回一个迭代值. range用法: range( 开始值,结束值,步长) 在需要大量迭代的时候,比较适合使用xrange()
- POJ2014 Flow Layout
Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 3161 Accepted: 2199 Description A f ...
- 【HDOJ6312】Game(博弈)
题意: 有一个1到n的序列,两个人轮流取数,取走一个数同时会取走它所有的因子,不能取者为输,两个人都按最优策略取数,问先手是否必胜 思路: #include<cstdio> #includ ...
- Pytho操作MySQL
一.相关代码 数据库配置类 MysqlDBConn.py 01 #encoding=utf-8 02 ''' 03 Created on 2012-11-12 04 05 @author: St ...
- HDU 5668 Circle
中国剩余定理. 可以手动模拟一下每一次开始的人的编号和结束的人的编号. 每次删掉一个人,对剩下的人重新编号. 这样一次模拟下来,可以得到n个方程 形如:(u[i]+k)%(n-i+1)=v[i] 化简 ...
- Linux 常用但较容易忘记的命令
看死循环 strace -p pid 查看系统版本 cat /etc/issue 设置内核启动版本 /etc/lilo.conf , /boot/grub/grub.conf 设置启动模式 /etc ...
- ABP每次生成前都执行bundle设置
ABP项目每次编译mvc项目时都会执行bundle,比较耗时. 可以在项目文件(*.csproj)中发现设置了每前生成前执行的命令 <Target Name="PreBuild&quo ...
- Spring基于Java的配置
以下内容引用自http://wiki.jikexueyuan.com/project/spring/java-based-configuration.html: 基于Java的配置选项,可以使你在不用 ...
- Java中的重写
以下内容引用自http://wiki.jikexueyuan.com/project/java/overriding.html: 如果一个类从它的父类继承了一个方法,如果这个方法没有被标记为final ...