Wallet的拖拉效果是不是很炫酷,笔者仿照着自己写了一个Demo, 效果还是可以滴!为什么要用CollectionView来写呢,因为我们可以自定义cell的layout 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的布局属性,包括frame与rect相交的cell.

- (NSArray *)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 *)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 *)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打开

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

CollectionViewFlowLayout.m

#define kExpandBottomHeight 50 // 展开cell的bottom距离屏幕bottom的距离,称之为footer吧

#define kExpandBottomFirstCellMarginOfTop 10 // footer里面第一个cell距离顶部的距离

#define kExpandBottomCellSpace 10 // footer里面第cell的间距

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

- (NSArray *)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先全部靠左,再执行动画,比较奇怪,暂时还解决不掉,等想到了再更新吧!

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

ios wallet开发_iOS: 使用UICollectionView实现Wallet效果相关推荐

  1. iOS动画开发之五——炫酷的粒子效果

    iOS动画开发之五--炫酷的粒子效果 在上几篇博客中,我们对UIView层的动画以及iOS的核心动画做了介绍,基本已经可以满足iOS应用项目中所有的动画需求,如果你觉得那些都还不够炫酷,亦或是你灵光一 ...

  2. ios wallet开发_iOS Wallet 开发

    Getting Started Introducing Wallet 以前 Wallet 叫 Passbook , Pass 有通行证,入场券的意思.苹果的 Passes 可以包含 图片, 条码, 也 ...

  3. ios 界面开发_iOS开发新手指南:界面-第一部分

    多年来,Apple凭借其iPhone和iPad系列一直是移动世界的领先行业. 尽管它可能在每次正式发布时都会大肆宣传,但它在移动平台中也占有很大的市场份额,这可能是大多数客户希望他们的应用程序存在于A ...

  4. iOS wallet开发

    wallet的前身为passbook,2015年WWDC大会苹果正式改名wallet,passbook是苹果2012年iOS6开放出来的新功能.可以帮助用户管理五种类型(Boarding passes ...

  5. ios wallet开发_Wallet app-想知道钱都花哪去了?试试它吧#iOS #Android

    有什么现象是人类观察到但是无法解释的呢?「女朋友为什么会生气」和「我的钱花到哪里去了」. 对于前一个问题,我没办法,因为我也不知道我家妹子为什么会生气,但是后一个问题,Wallet 可以帮你解决.Wa ...

  6. 从安装到部署的Cordova iOS应用开发说明

    Hybrid Application development for Android is a breeze, be it for development or production configur ...

  7. iOS应用开发最佳实践

    <iOS应用开发最佳实践> 基本信息 作者: 王浩 出版社:电子工业出版社 ISBN:9787121207679 上架时间:2013-7-22 出版日期:2013 年8月 开本:16开 页 ...

  8. 收集各种 iOS App 开发可以用到的代码示例

    code4app.com 这网站不错,收集各种 iOS App 开发可以用到的代码示例  cocoacontrols.com/ 英文版本的lib收集  objclibs.com/ 精品lib的收集网站 ...

  9. iOS移动开发周报-第22期

    iOS移动开发周报-第22期 [摘要]:本期iOS移动开发周报带来如下内容:苹果股价创新高,iOS8自动调整UITableView布局,Swift学习心得等. 新闻 <苹果股价创新高 市值全球第 ...

最新文章

  1. 深入Vue - 源码目录及构建过程分析
  2. Git的stash操作
  3. java中XPATH操作xml,非常便捷
  4. VBA 常用代码及自定义函数备忘
  5. 浅谈在过去的一年中,我所认识的前端开发---------
  6. 【英语学习】【Level 07】U06 First Time L2 A good food experience
  7. 【Kafka】Kafka ArithmeticException: / by zer
  8. navicat for MySQl破解版下载与安装
  9. 基于SSM的校园订餐管理系统
  10. firefox硬件加速 linux,火狐浏览器硬件加速相关资料以及开启关闭火狐硬件加速方法...
  11. MySQL窗口函数OVER()
  12. 什么是JAVA SWING
  13. 华硕双路服务器主板装系统,华硕双路服务器主板Z8PE-D12X
  14. 纯java的方式实现自定义自动化部署java项目
  15. Quartus Prime 与 Modelsim 调试 及do文件使用
  16. 【其他】【RQNOJ】同分异构体
  17. 关于git和 github
  18. python实现统计文本当中单词数量
  19. ES隔断时间会莫名其妙删除索引…………我头上一堆小朋友**喵喵机器人??还是病毒??
  20. [C++] 计算行列式的若干种方法

热门文章

  1. Python使用matplotlib可视化小提琴图、seaborn中的violinplot函数可视化多分类变量的小提琴图(Violin Plot)
  2. numpy使用[]语法索引二维numpy数组中指定数据行的数值内容(accessing the specific row in numpy array)
  3. python使用生成器生成浮点数列表、使用生成器生成(正)负的浮点数列表
  4. R语言使用ggplot2包geom_jitter()函数绘制分组(strip plot,一维散点图)带状图(自定义色彩、形状)实战
  5. python使用np.linspace函数生成均匀的浮点数列表实战:生成浮点数列表、生成浮点数列表(指定是否包含末尾值)
  6. 机器学习特征工程之连续变量离散化:连续变量二值化(Binarizer)
  7. HBase眼高手低从Shell到IDEA编程、心路笔记、踩坑过程
  8. asp连接mysql odbc,在ASP中连接MySQL数据库的方法,最好的通过ODBC方法
  9. java web 监听器 例子_Java web技术应用---监听器
  10. ubuntu18.04.4 pip3 换清华、阿里、豆瓣 源