动画二

隔了好多天,因为在忙别的事。。。
那我们现在要做什么,上一篇讲到一个滚动大屏图,巨丑。
趁着回忆的时间,先添两行代码

 //隐藏滚动条
 self.collectionView.showsVerticalScrollIndicator = NO;
 self.collectionView.showsHorizontalScrollIndicator = NO;

开始正题

让item变小

毫无疑问,这是一个很简单的事情。我们首先应该去设置itemSize,然后设置行(这里是纵向)间距,其次可以做一个组边距(sectionInset)使其两边留白。
当然简单的事情就要考虑的稍微多一点,这个size要怎么设置(200,300),这样可能在不同大小的屏幕上会有很明显的差别,那有人会说可以像Masonry(一个layout的框架)设置与其他UI的关系位置,这是一种策略,但是不见得好,因为它不是一个纯色的东西。根据其他位置拉伸变化(即便可以保持图片不拉伸)也不见得好,所以可以看很多代码会有一堆屏幕大小的宏,然后根据一个(200,300)针对某一尺寸的屏幕,对于不同的屏幕,按屏幕比等比扩大的代码。这个可以自行实现,我做简单易懂一点

#define ItemHeight (ScreenHeight * (5.0 / 9))
#define ItemWidth (ScreenWidth * (3.0 / 5))

然后相应的代码变成

self.itemSize = CGSizeMake(ItemWidth, ItemHeight);
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;   //水平方向
self.sectionInset = UIEdgeInsetsMake(0, ScreenWidth/2 - ItemWidth/2, 0, ScreenWidth/2 - ItemWidth/2);   //设置组边距
self.minimumLineSpacing = (ScreenWidth - ItemWidth)/4;

这里忘了提一个东西,我在这次加了一个pch(全局不需要自己导入的.h - -),当然里面放太多的东西会影响效率,至于添加方法自行百度,也可以做个.h自行导入,差不多

cell居中设置

现在我们的跑一下我们的代码,当你停止滑动的时候,它会停在一个当前的位置,但是这个位置可能是两个卡片(cell)的中间,或者偏一点的地方,我们需要的是对卡片进行操作,所以需要让它居中。所以引入一个问题,如果只是不小心的触碰到屏幕,稍稍的滑了一下,需要切换吗?显然是不需要的,把这个一下量化一下(设置为distance)以后,变成<distance的距离不需要切换,而>distance的距离就切换卡片。在这样的思路确定以后,我们开始实现这个逻辑
代理也是继承的,所以这样的效果应该在scrollView的代理里实现

//开始拖拽collectionView(scrollView的子类)
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
//停止拖拽collectionView
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

在这两个方法里面可以拿到起始的位置,和结束的位置,他们到底是怎么样的,可以在里面打印输出一下

//向左滑
2018-01-29 19:37:03.265 卡片转场1[31481:1202165] 开始拖动0.000000
2018-01-29 19:37:04.624 卡片转场1[31481:1202165] 停止拖动105.000000
2018-01-29 19:37:06.905 卡片转场1[31481:1202165] 开始拖动105.000000
2018-01-29 19:37:07.729 卡片转场1[31481:1202165] 停止拖动124.000000
2018-01-29 19:37:10.943 卡片转场1[31481:1202165] 开始拖动124.000000

//向右滑
2018-01-29 19:38:26.746 卡片转场1[31481:1202165] 开始拖动1449.000000
2018-01-29 19:38:28.164 卡片转场1[31481:1202165] 停止拖动1404.000000
2018-01-29 19:38:30.113 卡片转场1[31481:1202165] 开始拖动1404.000000
2018-01-29 19:38:30.547 卡片转场1[31481:1202165] 停止拖动1371.333333
2018-01-29 19:38:32.806 卡片转场1[31481:1202165] 开始拖动1371.333333

明白这样的变化以后,我们声明几个类内的变量

   CGFloat     _startDragX;        //开始移动的位置
   CGFloat     _endDragX;          //停止移动的位置
   CGFloat     _dragMiniDistance;  //最小移动的临界值
   NSInteger   _currentIndex;      //当前切换到的卡片索引位置
   NSInteger   _maxIndex;          //最大的索引位置
//在开始拖动的方法里获取到 _startDragX
_startDragX = scrollView.contentOffset.x;
//在停止拖动的方法里获取到_endDragX
_endDragX = scrollView.contentOffset.x;
然后根据上面的逻辑进行一系列的简单运算判断:
CGFloat delta = _startDragX - _endDragX;

if (delta >= _dragMiniDistance) {
    //向右滑动
    _currentIndex -= 1;  
 } else if (delta * -1 >= _dragMiniDistance) {
     //向左滑动
     _currentIndex += 1;
 }
    
 _currentIndex = _currentIndex <= 0 ? 0 : _currentIndex;
 _currentIndex = _currentIndex >= _maxIndex ? _maxIndex : _currentIndex;

这样我们就可以调用scrollToItemAtIndexPath: atScrollPosition这个方法实现滑动了,但是这里要注意一点,就是所有tableView,collectionView这些数据源方法的特殊性,如果你同步执行的话,他们的数据源方法可能还没有在内部算清,所以就会出Bug,所以对于这个方法,我们需要用主队列异步去执行,很多代码里面就把类似上面的一堆运算也扔在异步队列里,其实不用,我们只是要把UI更新放进去.

dispatch_async(dispatch_get_main_queue(), ^{
 [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:_currentIndex inSection:0]
 atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
 });

//顺带利用这个_currentIndex的索引,可以把背后的蒙版也换一下
self.BgView.image = [UIImage imageNamed:self.imageArray[_currentIndex].cardPicName];

调整布局属性

现在有个问题需要我们考虑,就是每个卡片的大小都是一样,看示例gif明显当前屏幕中间的大,两边的小。所以我们也要这样做,调整cell的大小,在UICollectionView里面,他们的大小(还有透明度这些属性)是归于布局属性负责的,所以我们要调整布局属性,在哪里调整,当然是代理方法了,一个UICollectionViewFlowLayout的父类代理UICollectionViewLayout里面有layoutAttributesForElementsInRect:(CGRect)rect这样一个方法,是这样介绍的
//Implement -layoutAttributesForElementsInRect:
//to return layout attributes for for supplementary or decoration views, or to perform layout in an as-needed-on-screen fashion.
翻译过来就是:返回 追加的 或者 装饰视图 布局属性 或者去适应屏幕上的布局。
还有一个这样的方法:
-(UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath
很明显是对每个具体cell的布局进行调整
这里我们的需求其实是对两边cell的大小进行统一的调整,其实也不用具体到张三,李四cell这个具体的cell上,只要是在两边的就进行缩放就好了,所以选择第一个方法去实现

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect
{
    //这样就拿到了所有的布局属性
    NSArray<UICollectionViewLayoutAttributes *> *arr = [super layoutAttributesForElementsInRect:rect].copy;
    
    return arr;
}

这里同样有个问题需要说明,就是苹果规定,我们不能直接去拿系统生成这些原有的布局属性,对这些原有的布局进行更改,直接更改会报错(我们不展开去解释这个问题),所以我用了.copy,其实这样用也不好,在你运行的时候,还是会给你一堆警告

Logging only once for UICollectionViewFlowLayout cache mismatched frame
UICollectionViewFlowLayout has cached frame mismatch for index path <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0} - cached value: {{82.666666666666686, 163.66667320702987}, {248.39999999999998, 408.88887580816248}}; expected value: {{82.666666666666671, 163.66666666666666}, {248.39999999999998, 408.88888888888891}}
This is likely occurring because the flow layout subclass CardFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them

就是告诉你这样不好,但是通常好像也不会出错,但是为了避免这样,我们可以干脆做一个拷贝的方法

- (NSArray *)getCopyOfAttributes:(NSArray *)attributes {
    NSMutableArray *copyArr = [NSMutableArray new];
    for (UICollectionViewLayoutAttributes *attribute in attributes) {
        [copyArr addObject:[attribute copy]];
    }
    return copyArr;
}

这样就ok了。
接着写我们的代码,这里单独说一点collectionView的位置不是屏幕大小的位置,它是内部展开的位置

    //获取屏幕中线
    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width / 2.0;
    //拿到每个属性
    for (UICollectionViewLayoutAttributes *attr in arr) {
       
        //布局属性与中线的距离
        CGFloat distance = fabs(attr.center.x - centerX);
        //距离与屏幕宽的比例(为了计算缩放的比例)
        CGFloat disScale = distance / self.collectionView.bounds.size.width;
        //确定缩放的大小
        CGFloat scale = fabs(cos(disScale * M_PI / 4));
        
        //对布局属性进行缩放变换
        //如果想要3D倾斜的变化,也可以在这里指定为3D变化
        attr.transform = CGAffineTransformMakeScale(1.0, scale);
        
        //同时也利用这个比例对透明度进行一下更改,显得自然
        attr.alpha = scale;
    
    }

但是这样运行以后我们又发现一个问题,就是他们好像是一顺缩小的,越来越小,一看我们的代码,是啊,我们设置的就是把属性缩小了,缩小的属性的后面的属性就变得更小了。所以我们要调整代码,让他们下次恢复之前的约束,然后再进行变化,刚好有这样一个方法:- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
//判定为布局需要被无效化并重新计算的时候,布局对象会被询问以提供新的布局
所以我们只需要在这里返回YES就好了。
这样这篇文章就可以完了

3月-12-2018 17-00-40.gif

再下面就要对cell内部进行复杂操作了。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,721评论 1 92
  • 在上篇文章 属性动画(一) 中已经对属性动画有了基本的介绍,本篇文章将对属性动画中稍微高级点的内容进行介绍,主要介...
    lijiankun24阅读 338评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,274评论 25 707
  • 1 懒加载 懒加载与OC中的懒加载的区别:懒加载的类一旦 设置为nil 后, 懒加载就不会再次执行,与OC中不同,...
    JunShine阅读 219评论 0 0
  • 2011年9月,作为大一懵懂的小学妹,由着自己想作为志愿者的情愫,过关斩将成为红十字协会的一员,便遇上了此生...
    傻子薛阅读 185评论 0 1