美文网首页iOS开发资料收集区iOS DeveloperiOS动画
UICollectionView实现放大缩小动画效果的左右滑动

UICollectionView实现放大缩小动画效果的左右滑动

作者: LDYG14 | 来源:发表于2017-09-04 18:04 被阅读997次

    公司目前做一个需要左右滑动放大缩小功能的实现, 在此记录一下.


    效果显示图.png

    在利用UICollectionView来展示,那就需要自定义UICollectionViewFlowLayout

    在整个实现过程中, 存在几个关键的问题点:

    1. 如何根据两个item之间的间距,在滑动过程中实现放大缩小的比例值?
    2. 当手停止拖拽时, 如何让UICollectionView定位显示离中心点最近的一个item?
    3. 如何根据随之定位的item位置, 改动对应下方/上方位置的内容?

    问题一:根据两个item之间的间距,在滑动过程中实现放大缩小的比例值

    计算放大缩小比例.png

    核心在于: 计算UICollectionView中的centerX 的值与显示在屏幕上的每个item的centerX之间的差值

    问题二:当手停止拖拽时, 如何让UICollectionView定位显示离中心点最近的一个item

    定位距离中心点最近的item.png

    上述截图中的方法

    • (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity 用于 控制显示最终区域
      --------proposedContentOffset 就是UICollectionView当前的偏移量
      核心在于: 计算UICollectionView中的centerX的值与显示在屏幕上的每个item比较距离大小找到最小的间距值

    在滚动过程中图片的间距和item的宽和高是动态计算变化的

    cell宽高和间距.png

    图解:


    计算距离中心点的间距.png

    问题三:根据随之定位的item位置, 改动对应下方/上方位置的内容

    监听scrollview的滚动.png 处理scrollview的滚动.png 处理滚动停止操作.png

    核心处理: 在判断滚动的距离值 = item的宽度 + 两个item之间的间距
    285 = 235(Width) + 50(间距)
    间距计算: 记录起始点与结束点.
    注意: 这里在手动拖拽还为完全静止时设置scrollview不能滚动
    原因在于: 如果在还未停止时就继续手动拖拽的话就导致计算的偏移量不准确,从而造成对应的内容对不上.
    最后需要在完全静止时,将scrollview的滚动设置回正常情况.

    到此实现左右切换item时放大缩小效果完成 !!

    附上自定义layout文件中的代码

    "#import "FIDCollectionViewFlowLayout.h"
    //居中卡片宽度与据屏幕宽度比例
    static float CardWidthScale = 0.63f;
    static float CardHeightScale = 1.0f;
    @implementation FIDCollectionViewFlowLayout
    初始化方法

    - (void)prepareLayout {

    [super prepareLayout];
    self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.sectionInset = UIEdgeInsetsMake(0, [self collectionInset], 0, [self collectionInset]);
    

    }

    设置缩放动画

    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {

    //扩大控制范围,防止出现闪屏现象
    CGRect bigRect = rect;
    bigRect.size.width = rect.size.width + 2*[self cellWidth];
    bigRect.origin.x = rect.origin.x - [self cellWidth];
    
    NSArray *arr = [self getCopyOfAttributes:[super layoutAttributesForElementsInRect:bigRect]];
    //屏幕中线
    CGFloat centerX = self.collectionView.contentOffset.x + self.collectionView.bounds.size.width/2.0f;
    //刷新cell缩放
    for (UICollectionViewLayoutAttributes *attributes in arr) {
        CGFloat distance = fabs(attributes.center.x - centerX);
        //移动的距离和屏幕宽度的的比例
        CGFloat apartScale = distance/self.collectionView.bounds.size.width;
        //把卡片移动范围固定到 -π/4到 +π/4这一个范围内
        CGFloat scale = fabs(cos(apartScale * M_PI/4));
        //设置cell的缩放 按照余弦函数曲线 越居中越趋近于1
        attributes.transform = CGAffineTransformMakeScale(scale, scale);
    }
    return arr;
    

    }

    pragma mark 配置方法

    卡片宽度

    - (CGFloat)cellWidth {

      return self.collectionView.bounds.size.width * CardWidthScale;
    

    }

    卡片间隔

    - (float)cellMargin {

    return (self.collectionView.bounds.size.width - [self cellWidth])/7;
    

    }

    设置左右缩进

    - (CGFloat)collectionInset {

    return self.collectionView.bounds.size.width/2.0f - [self cellWidth]/2.0f;
    

    }

    pragma mark 约束设定

    //最小纵向间距

    - (CGFloat)minimumLineSpacing {

    return [self cellMargin];
    

    }
    //cell大小

    - (CGSize)itemSize {

    return CGSizeMake([self cellWidth],self.collectionView.bounds.size.height * CardHeightScale);
    

    }

    pragma mark 其他设定

    //是否实时刷新布局

    - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {

    return true;
    

    }

    //防止报错 先复制attributes

    - (NSArray *)getCopyOfAttributes:(NSArray *)attributes {

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

    }

    // 控制显示最终区域

    - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity

    {

    CGRect visableRect = CGRectMake(proposedContentOffset.x, 0,self.collectionView.width , self.collectionView.height);
    
    // 中心轴x
    CGFloat centerX = proposedContentOffset.x + self.collectionView.width * 0.5;
    
    NSArray *attrArr = [super layoutAttributesForElementsInRect:visableRect];
    
    CGFloat minDelta = MAXFLOAT;
    
    for (UICollectionViewLayoutAttributes *attr in attrArr) {
        
        CGFloat delta = fabs(attr.center.x - centerX);
        
        if (delta < fabs(minDelta)) {
            minDelta = attr.center.x - centerX;
        }
    }
    proposedContentOffset.x += minDelta;
    
    if (proposedContentOffset.x <= 0) {
        proposedContentOffset.x = 0;
    }
    
    return proposedContentOffset;
    

    }

    @end

    参考链接:
    http://blog.csdn.net/u013282507/article/details/54136812
    http://blog.csdn.net/u013282507/article/details/53103816

    欢迎指正!

    毛姆说的,阅读能为自己筑起一个避难所,几乎可以避开生命中所有的灾难。

    欢迎关注我的微信公众号:LDYG2017, 或扫描下方二维码关注. 这里会分享我的读书笔记, 愿你我共同进步.

    初始化蒲公英.jpg

    相关文章

      网友评论

        本文标题:UICollectionView实现放大缩小动画效果的左右滑动

        本文链接:https://www.haomeiwen.com/subject/yhasjxtx.html