美文网首页CollectionviewiOS 效果demo例子iOS 知识点
iOS: 使用UICollectionView实现Wallet效

iOS: 使用UICollectionView实现Wallet效

作者: 刘奶奶去买榴莲和牛奶 | 来源:发表于2017-06-29 18:10 被阅读1280次

    Wallet的拖拉效果是不是很炫酷,笔者仿照着自己写了一个Demo, 效果还是可以滴!为什么要用CollectionView来写呢,因为我们可以自定义celllayout attributes,如果cell比较少的话,用scrollview自定义也是可以的,相对更容易控制!

    效果.gif

    下面由浅入深,一步步的教你怎么做,非常好理解.

    第一步:创建一个UICollectionView

    基础代码就不贴了,想看的文章末尾有完整的Demo. 对CollectionView不熟悉的同学可以先去补补课,关于这方面的博客很多.

    列一下核心代码吧.

    子类化UICollectionViewFlowLayout

    #import "CollectionViewFlowLayout.h"
    #define kCollectionViewWidth (self.collectionView.frame.size.width)
    #define kCollectionViewHeight (self.collectionView.frame.size.height)
    
    #define kCellHeight 200 /// cell 高度
    #define kCellSpace 100 // cell0Top to cell1Top
    #define kComeUpAnimationDuration 0.25
    #define kBottomAutoScrollDistance 200
    #define kTopAutoScrollDistance 100
    #define kCanMoveDistance 30
    #define kExpandBottomHeight 50 // 展开cell的bottom距离屏幕bottom的距离,称之为footer吧
    #define kExpandBottomFirstCellMarginOfTop 10 // footer里面第一个cell距离顶部的距离
    #define kExpandBottomCellSpace 10 // footer里面第cell的间距
    
    #if TARGET_IPHONE_SIMULATOR
    #define kAutoScrollSpeed 10
    #elif TARGET_OS_IPHONE
    #define kAutoScrollSpeed 4
    #endif
    
    @implementation CollectionViewFlowLayout
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            
        }
        return self;
    }
    
    - (void)prepareLayout {
        [super prepareLayout];
        self.itemSize = CGSizeMake(kCollectionViewWidth - inset.right - inset.left, kCellHeight);
        self.minimumLineSpacing = kCellSpace - kCellHeight;
    
    }
    
    - (CGSize)collectionViewContentSize {
        UIEdgeInsets inset = self.collectionView.contentInset;
        NSInteger items = [self.collectionView numberOfItemsInSection:0];
        CGSize size = CGSizeMake(self.collectionView.frame.size.width - inset.left - inset.right, (items - 1) * kCellSpace + kCellHeight- inset.bottom);
        return size;
    }
    

    这个是关键方法,我们默认只有一个section.这个方法需要返回rect范围之内的cell的布局属性,包括framerect相交的cell.

    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
        NSInteger rows = [self.collectionView numberOfItemsInSection:0];
        CGFloat offsetY = self.collectionView.contentOffset.y;
        UIEdgeInsets inset = self.collectionView.contentInset;
        NSMutableArray *attrs = [NSMutableArray arrayWithCapacity:0];\
        // 与rect相交的最大最小item值
        int minRow = MAX(0, floor((offsetY)  / kCellSpace));
        int maxRow = MIN(ceil((offsetY + self.collectionView.frame.size.height)  / kCellSpace), rows);
        for (int row = minRow; row < maxRow; row++) {
            // 顶部只留一个cell
            if (row * kCellSpace >= offsetY - kCellSpace) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
                UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
                CGRect cellRect = CGRectMake(0, row * kCellSpace, self.collectionView.frame.size.width - inset.right, kCellHeight);
                att.frame = cellRect;
                // 因为我们的cell有重叠,必须设置zIndex,否则复用时层级会有问题
                att.zIndex = att.indexPath.item * 2;
                att.transform3D = CATransform3DMakeTranslation(0, 0, att.indexPath.item * 2);
                if (CGRectIntersectsRect(cellRect, rect) || CGRectContainsRect(cellRect, rect)) {
                    [attrs addObject:att];
                }
            }
        }
        return attrs;
    }
    
    @end
    

    这个时候看下效果:

    9F80A5DD-E8B0-4052-AE07-E53C1DA92E05.png

    第二步: 实现cell吸附在顶部的效果

    这个时候我们就要控制滑动时cell的坐标,需要在FlowLayout里面重写这个方法:

    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
        NSInteger rows = [self.collectionView numberOfItemsInSection:0];
        CGFloat offsetY = self.collectionView.contentOffset.y;
        UIEdgeInsets inset = self.collectionView.contentInset;
        NSMutableArray *attrs = [NSMutableArray arrayWithCapacity:0];\
        // 与rect相交的最大最小item值
        int minRow = MAX(0, floor((offsetY)  / kCellSpace));
        int maxRow = MIN(ceil((offsetY + self.collectionView.frame.size.height)  / kCellSpace), rows);
        for (int row = minRow; row < maxRow; row++) {
            // 顶部只留一个cell
            if (row * kCellSpace >= offsetY - kCellSpace) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
                UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
                CGRect cellRect = CGRectMake(0, MAX(offsetY, row * kCellSpace), self.collectionView.frame.size.width - inset.right, kCellHeight);
                att.frame = cellRect;
                // 因为我们的cell有重叠,必须设置zIndex,否则复用时层级会有问题
                att.zIndex = att.indexPath.item * 2;
                att.transform3D = CATransform3DMakeTranslation(0, 0, att.indexPath.item * 2);
                if (CGRectIntersectsRect(cellRect, rect) || CGRectContainsRect(cellRect, rect)) {
                    [attrs addObject:att];
                }
            }
        }
        return attrs;
    }
    

    另外我们需要重新刷新布局,所以需要重写这个方法

    - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
        return YES;
    }
    

    哎,顶部的cell可以重叠了

    1.gif

    第三步: 实现下拉的弹簧效果

    同样是在上面的方法中增加代码:

    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
        NSInteger rows = [self.collectionView numberOfItemsInSection:0];
        CGFloat offsetY = self.collectionView.contentOffset.y;
        UIEdgeInsets inset = self.collectionView.contentInset;
        NSMutableArray *attrs = [NSMutableArray arrayWithCapacity:0];\
        // 与rect相交的最大最小item值
        int minRow = MAX(0, floor((offsetY)  / kCellSpace));
        int maxRow = MIN(ceil((offsetY + self.collectionView.frame.size.height)  / kCellSpace), rows);
        for (int row = minRow; row < maxRow; row++) {
            // 顶部只留一个cell
            if (row * kCellSpace >= offsetY - kCellSpace) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
                UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
                CGRect cellRect = CGRectMake(0, MAX(offsetY, row * kCellSpace), self.collectionView.frame.size.width - inset.right, kCellHeight);
                if (offsetY < -inset.top) {
                    // 0.25是相对于offsetY的偏移比例,根据需要自行调节
                    cellRect.origin.y = att.indexPath.item * kCellSpace - fabs(offsetY + inset.top) + fabs(offsetY + inset.top) * att.indexPath.item * 0.25;
                }
                att.frame = cellRect;
                // 因为我们的cell有重叠,必须设置zIndex,否则复用时层级会有问题
                att.zIndex = att.indexPath.item * 2;
                att.transform3D = CATransform3DMakeTranslation(0, 0, att.indexPath.item * 2);
                if (CGRectIntersectsRect(cellRect, rect) || CGRectContainsRect(cellRect, rect)) {
                    [attrs addObject:att];
                }
            }
        }
        return attrs;
    }
    

    现在成这样了:

    2.gif

    第四步: 长按cell进行移动

    这部分的代码比较复杂

    首先给cell添加一个长按手势,然后在FlowLayout里面处理手势的执行! 然后对要移动的cell进行截图,把这个ImageView添加到当前cell上面,当截图进行移动时,通过坐标来判断当前cell附近的cell是否满足交换条件,如果满足交换条件,则调用- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;方法进行交换,交换完成要更新层级!

    另外,如果当前cell处于屏幕的底端或者顶端时,要让CollectionView进行滚动!

    因为顶部的cell有吸附的效果,所以在进行顶部滚动交换时会闪屏,没有达到理想的交换效果,笔者暂时还无法解决,希望有大神可以指点下! 所以退而求其次,目前交换时会停止CollectionView的滚动.

    下面是具体实现:

    #define kCollectionViewWidth (self.collectionView.frame.size.width)
    #define kCellHeight 200 /// cell 高度
    #define kCellSpace 100 // cell0Top to cell1Top
    #define kComeUpAnimationDuration 0.25
    #define kBottomAutoScrollDistance 200
    #define kTopAutoScrollDistance 100
    #define kCanMoveDistance 30
    
    #if TARGET_IPHONE_SIMULATOR
    #define kAutoScrollSpeed 10
    #elif TARGET_OS_IPHONE
    #define kAutoScrollSpeed 4
    #endif
    
    typedef NS_ENUM(NSInteger, CollectionViewAutoScrollType){
        CollectionViewAutoScrollNone = 0,
        CollectionViewAutoScrollUp, // 向上
        CollectionViewAutoScrollDown, // 向下
    };
    
    @interface CollectionViewFlowLayout ()
    @property (nonatomic, strong) UIImageView *shootImageView;
    
    @property(assign, nonatomic) CGPoint longPressGesLastLocation;
    @property (nonatomic, strong) CADisplayLink *displayLink;
    @property(assign, nonatomic) BOOL isMoveing;
    @property (nonatomic, strong) NSIndexPath *currentIndexPath;
    @property(weak, nonatomic) CollectionViewCell *currentCell;
    @property(assign, nonatomic) CollectionViewAutoScrollType scrollType;
    
    @end
    

    手势的执行方法:

    #pragma mark -
    #pragma mark -- -- -- -- -- - CollectionViewCell Delegate - -- -- -- -- --
    - (void)collectionViewCell:(CollectionViewCell *)cell handlerLongPressGesture:(UILongPressGestureRecognizer *)ges {
        switch (ges.state) {
            case UIGestureRecognizerStateBegan:
            {
                // 对cell进行截图
                NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
                self.currentIndexPath = indexPath;
                self.currentCell = cell;
                self.isMoveing = NO;
                if (!self.shootImageView) {
                    self.shootImageView = [UIImageView new];
                }
                self.shootImageView.image = [self screenshotWithView:cell];
                self.shootImageView.frame = cell.frame;
                self.shootImageView.layer.transform = CATransform3DMakeTranslation(0, 0, indexPath.item * 2 + 1);
                [self.collectionView addSubview:self.shootImageView];
                // 让截图浮出来
                cell.hidden = YES;
                [UIView animateWithDuration:kComeUpAnimationDuration animations:^{
                    CGRect frame = self.shootImageView.frame;
                    frame.origin.y -= 30;
                    self.shootImageView.frame = frame;
                } completion:^(BOOL finished) {
                }];
                self.longPressGesLastLocation = [ges locationInView:self.collectionView];
            }
                break;
            case UIGestureRecognizerStateChanged:
            {
                // 移动view
                CGPoint location = [ges locationInView:self.collectionView];
                CGFloat translateY = location.y - self.longPressGesLastLocation.y;
                CGRect frame = self.shootImageView.frame;
                
                frame.origin.y += translateY;
                self.shootImageView.frame = frame;
                
                // 如果滑到头则进行滚动
                CGFloat bottom = frame.origin.y - self.collectionView.contentOffset.y - self.collectionView.frame.size.height;
                CGFloat top = frame.origin.y - self.collectionView.contentOffset.y;
                
                if (self.scrollType == CollectionViewAutoScrollNone) {
                    // 根据第一次的手势来判断执行那种滚动
                    BOOL shouldAutoScrollDown = fabs(top) < kTopAutoScrollDistance && translateY < -0.5;
                    BOOL shouldAutoScrollUp = fabs(bottom) < kBottomAutoScrollDistance && translateY > 0.5;
                    if (shouldAutoScrollDown) {
                        self.scrollType = CollectionViewAutoScrollDown;
                    } else if(shouldAutoScrollUp) {
                        self.scrollType = CollectionViewAutoScrollUp;
                    } else {
                        self.scrollType = CollectionViewAutoScrollNone;
                    }
                    // 处于顶部或者底部的滚动范围之内不做处理
                    if (fabs(top) > kTopAutoScrollDistance && fabs(bottom) > kBottomAutoScrollDistance) {
                        [self handlerMoveItemAction];
                    }
                } else {
                    // 滚动中则只根据距离来判断
                    BOOL shouldAutoScrollDown = fabs(top) < kTopAutoScrollDistance;
                    BOOL shouldAutoScrollUp = fabs(bottom) < kBottomAutoScrollDistance;
                    if (shouldAutoScrollDown) {
                        self.scrollType = CollectionViewAutoScrollDown;
                    } else if(shouldAutoScrollUp) {
                        self.scrollType = CollectionViewAutoScrollUp;
                    } else {
                        self.scrollType = CollectionViewAutoScrollNone;
                    }
                }
    
                if (self.scrollType != CollectionViewAutoScrollNone) {
                    if (!self.displayLink) {
                        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkAction:)];
                        [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
                    }
                } else {
                    [self.displayLink invalidate];
                    self.displayLink = nil;
                }
                self.longPressGesLastLocation = [ges locationInView:self.collectionView];
            }
                break;
            case UIGestureRecognizerStateEnded:
            case UIGestureRecognizerStateCancelled:
            case UIGestureRecognizerStateFailed:
            {
                [self.displayLink invalidate];
                self.displayLink = nil;
                self.scrollType = CollectionViewAutoScrollNone;
                [UIView animateWithDuration:kComeUpAnimationDuration animations:^{
                    CGRect frame = cell.frame;
                    self.shootImageView.frame = frame;
                } completion:^(BOOL finished) {
                    [self.shootImageView removeFromSuperview];
                    cell.hidden = NO;
                }];
            }
                break;
            default:
                break;
        }
    }
    
    

    计时器执行方法:

    
    #pragma mark -
    #pragma mark -- -- -- -- -- - Event Response - -- -- -- -- --
    - (void)displayLinkAction:(CADisplayLink *)link {
        // 滚倒底了已经
        if (self.scrollType == CollectionViewAutoScrollUp && self.collectionView.contentOffset.y + self.collectionView.frame.size.height > self.collectionView.contentSize.height) {
            return;
        }
        
        // 滚到顶了
        if (self.scrollType == CollectionViewAutoScrollDown && self.collectionView.contentOffset.y < 0) {
            return;
        }
        
        if (self.isMoveing) {
            return;
        }
        
        CGFloat increaseValue;
        
        if (self.scrollType == CollectionViewAutoScrollUp) {
            // 让collectionView刚好可以滚到底
            increaseValue = MIN(kAutoScrollSpeed, self.collectionView.contentSize.height - self.collectionView.frame.size.height - self.collectionView.contentOffset.y);
        } else {
            increaseValue = MAX(-kAutoScrollSpeed, -self.collectionView.contentOffset.y);
        }
        
        CGRect frame = self.shootImageView.frame;
        frame.origin.y += increaseValue;
        self.shootImageView.frame = frame;
        
        CGPoint point = self.longPressGesLastLocation;
        point.y += increaseValue;
        self.longPressGesLastLocation = point;
    
        CGPoint offset = self.collectionView.contentOffset;
        offset.y += increaseValue;
        [self.collectionView setContentOffset:offset];
    
        /// TODO: 优化顶部的交换
        [self handlerMoveItemAction];
    }
    

    执行交换

    - (void)handlerMoveItemAction {
        if (!self.isMoveing) {
            // 取到当前cell附近的cell, 判断是否可以交换
            BOOL shouldMove = NO;
            NSIndexPath *preferIndexPath;
            CollectionViewCell *preferCell;
            preferIndexPath = [NSIndexPath indexPathForItem:self.currentIndexPath.item + 1 inSection:self.currentIndexPath.section];
            preferCell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:preferIndexPath];
    
            // 间距小于`kCanMoveDistance`开始交换
            if (fabs(preferCell.frame.origin.y - self.shootImageView.frame.origin.y) < kCanMoveDistance) {
                shouldMove = YES;
            } else {
                preferIndexPath = [NSIndexPath indexPathForItem:self.currentIndexPath.item - 1 inSection:self.currentIndexPath.section];
                preferCell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:preferIndexPath];
                if (fabs(preferCell.frame.origin.y - self.shootImageView.frame.origin.y) < kCanMoveDistance) {
                    shouldMove = YES;
                } else {
                    return;
                }
            }
        
            if (shouldMove && preferCell && preferIndexPath) {
                [self.collectionView performBatchUpdates:^{
                    self.isMoveing = YES;
                    [self.collectionView moveItemAtIndexPath:self.currentIndexPath toIndexPath:preferIndexPath];
                    
                    if ([self.delagate respondsToSelector:@selector(collectionViewFlowLayout:moveItemAtIndexPath:toIndexPath:)]) {
                        // 通知vc交换数据源
                        [self.delagate collectionViewFlowLayout:self moveItemAtIndexPath:self.currentIndexPath toIndexPath:preferIndexPath];
                    }
                    // 完成之后更新transform
                    self.shootImageView.layer.transform = CATransform3DMakeTranslation(0, 0, preferIndexPath.row * 2 + 1);
                    // 这个地方需要重新设置层级,更改transform无用?
                    if (preferIndexPath.item > self.currentIndexPath.item) {
                        [self.collectionView insertSubview:self.currentCell aboveSubview:preferCell];
                    } else {
                        [self.collectionView insertSubview:preferCell aboveSubview:self.currentCell];
                    }
                    self.currentIndexPath = preferIndexPath;
                    if (self.scrollType == CollectionViewAutoScrollDown) {
                        // 头部有很多的cell,交换太快会闪屏,需要有间隔
                        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                            self.isMoveing = NO;
                        });
                    } else {
                        self.isMoveing = NO;
                    }
                } completion:^(BOOL finished) {
    
                }];
            }
        }
    }
    
    
    

    对cell进行截图:

    - (UIImage *)screenshotWithView:(UIView *)view {
        UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, [UIScreen mainScreen].scale);
        [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
    

    效果:

    3.gif

    可以看到滚动时向下交换还是很流畅的,但是向上交换是第一个cell会闪一下!

    第五步: 点击cell打开

    我们只需要重新设置可见范围内的cellUICollectionViewLayoutAttributes,然后调用-(void)invalidateLayout;

    CollectionViewFlowLayout.m

    #define kExpandBottomHeight 50 // 展开cell的bottom距离屏幕bottom的距离,称之为footer吧
    #define kExpandBottomFirstCellMarginOfTop 10 // footer里面第一个cell距离顶部的距离
    #define kExpandBottomCellSpace 10 // footer里面第cell的间距
    

    添加展开时候的布局属性:

    - (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
        NSInteger rows = [self.collectionView numberOfItemsInSection:0];
        CGFloat offsetY = self.collectionView.contentOffset.y;
        UIEdgeInsets inset = self.collectionView.contentInset;
        NSMutableArray *attrs = [NSMutableArray arrayWithCapacity:0];\
        // 与rect相交的最大最小item值
        int minRow = MAX(0, floor((offsetY)  / kCellSpace));
        int maxRow = MIN(ceil((offsetY + self.collectionView.frame.size.height)  / kCellSpace), rows);
        int shrinkCellIndex = 0; // 缩起来cell的下标,0, 1, 2, 3
        for (int row = minRow; row < maxRow; row++) {
            // 顶部只留一个cell
            if (row * kCellSpace >= offsetY - kCellSpace) {
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:row inSection:0];
                UICollectionViewLayoutAttributes *att = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
                CGRect cellRect = CGRectMake(0, MAX(offsetY, row * kCellSpace), kCollectionViewWidth - inset.right, kCellHeight);
                CGFloat left = (4 - shrinkCellIndex) * 5;
                CGFloat top = shrinkCellIndex * kExpandBottomCellSpace + kExpandBottomFirstCellMarginOfTop;
                if (self.isExpand) {
                    if (indexPath.item == self.currentIndexPath.item) {
                        cellRect = CGRectMake(0, offsetY, kCollectionViewWidth - inset.right, kCollectionViewHeight - kExpandBottomHeight);
                        self.currentCellShrinkMarginLeft = left;
                    } else {
                        cellRect = CGRectMake(left, offsetY + (kCollectionViewHeight - kExpandBottomHeight) + top, kCollectionViewWidth - inset.right - left * 2, kCellHeight);
                        shrinkCellIndex++;
                        shrinkCellIndex = MIN(shrinkCellIndex, 3);
                    }
                } else {
                    if (offsetY < -inset.top) {
                        // 0.25是相对于offsetY的偏移比例,根据需要自行调节
                        cellRect.origin.y = att.indexPath.item * kCellSpace - fabs(offsetY + inset.top) + fabs(offsetY + inset.top) * att.indexPath.item * 0.25;
                    }
                }
                att.frame = cellRect;
                att.center = CGPointMake(CGRectGetMinX(cellRect) + CGRectGetWidth(cellRect) / 2, CGRectGetMinY(cellRect) + CGRectGetHeight(cellRect) / 2);
                // 因为我们的cell有重叠,必须设置zIndex,否则复用时层级会有问题
                att.zIndex = att.indexPath.item * 2;
                att.transform3D = CATransform3DMakeTranslation(0, 0, att.indexPath.item * 2);
                if (CGRectIntersectsRect(cellRect, rect) || CGRectContainsRect(cellRect, rect)) {
                    [attrs addObject:att];
                }
            }
        }
        return attrs;
    }
    
    

    点击展开cell:

    #pragma mark -
    #pragma mark -- -- -- -- -- - ExpandCell - -- -- -- -- --
    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
        if (!self.isExpand) {
            [self expandCellWithIndexPath:indexPath];
        } else {
            [self closeCellWithoutAnimation];
        }
    }
    
    - (void)expandCellWithIndexPath:(NSIndexPath *)indexPath {
        self.currentIndexPath = indexPath;
        self.currentCell = (CollectionViewCell *)[self.collectionView cellForItemAtIndexPath:indexPath];
        self.currentCell.panGes.enabled = YES;
        self.isExpand = YES;
        
        [self.collectionView performBatchUpdates:^{
            [self invalidateLayout];
        } completion:^(BOOL finished) {
            self.collectionView.scrollEnabled = NO;
        }];
    
    }
    
    - (void)closeCellWithoutAnimation {
        self.isExpand = NO;
        self.currentCell.panGes.enabled = NO;
        [self.collectionView performBatchUpdates:^{
            [self invalidateLayout];
        } completion:^(BOOL finished) {
            self.collectionView.scrollEnabled = YES;
        }];
    }
    
    - (void)closeCell {
        // 结束
        UIEdgeInsets inset = self.collectionView.contentInset;
        [UIView animateWithDuration:kComeUpAnimationDuration animations:^{
            self.currentCell.frame = CGRectMake(self.currentCellShrinkMarginLeft, kCollectionViewHeight, kCollectionViewWidth - inset.right - self.currentCellShrinkMarginLeft * 2, kCollectionViewHeight - kExpandBottomHeight);
        }completion:^(BOOL finished) {
            [self closeCellWithoutAnimation];
        }];
    }
    
    

    处理滑动手势:

    - (void)collectionViewCell:(CollectionViewCell *)cell handlerPanGesture:(UIPanGestureRecognizer *)ges {
        CGFloat offsetY = self.collectionView.contentOffset.y;
        UIEdgeInsets inset = self.collectionView.contentInset;
        switch (ges.state) {
            case UIGestureRecognizerStateBegan:
            {
                
            }
                break;
            case UIGestureRecognizerStateChanged:
            {
                CGPoint translate = [ges translationInView:cell];
                CGPoint velocity = [ges velocityInView:cell];
                if (velocity.y > 1300) {
                    [ges setEnabled:NO];
                    [self closeCell];
                    return;
                }
                CGRect frame = cell.frame;
                CGFloat pecent = (frame.origin.y - offsetY) / (kCollectionViewHeight - kExpandBottomHeight);
                CGFloat left = pecent * self.currentCellShrinkMarginLeft;
                [cell setTransform:CGAffineTransformTranslate(cell.transform, 0, translate.y)];
                frame = CGRectMake(left, frame.origin.y + translate.y, kCollectionViewWidth - inset.right - left * 2,kCollectionViewHeight - kExpandBottomHeight );
                cell.frame = frame;
                [ges setTranslation:CGPointZero inView:cell];
            }
                break;
            case UIGestureRecognizerStateEnded:
            case UIGestureRecognizerStateCancelled:
            case UIGestureRecognizerStateFailed:
            {
                
                if (cell.frame.origin.y > kCollectionViewHeight / 2 - kExpandBottomHeight / 2) {
                    // 结束
                    [self closeCell];
                } else {
                    // 复位
                    [UIView animateWithDuration:kComeUpAnimationDuration animations:^{
                        cell.frame = CGRectMake(0, offsetY, kCollectionViewWidth - inset.right, kCollectionViewHeight - kExpandBottomHeight);
                    }];
                }
            }
                break;
            default:
                break;
        }
    }
    
    

    效果如下:

    4.gif

    但是展开的时候cell先全部靠左,再执行动画,比较奇怪,暂时还解决不掉,等想到了再更新吧!

    文中如果有什么错误, 或者有什么好的想法,都可以留言! 我会更新!

    转载请注明出处!
    http://www.jianshu.com/p/b0fa1daa8665

    GitHub:
    https://github.com/TactBoy/ios-spring-shrink-move-expand-collectionView

    相关文章

      网友评论

      本文标题:iOS: 使用UICollectionView实现Wallet效

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