Wallet的拖拉效果是不是很炫酷,筆者仿照著自己寫了一個Demo, 效果還是可以滴!為什么要用CollectionView來
寫呢,因?yàn)槲覀兛梢宰远xcell
的layout attributes
,如果cell
比較少的話,用scrollview
自定義也是可以的,相對更容易控制!
下面由淺入深,一步步的教你怎么做,非常好理解.
第一步:創(chuàng)建一個UICollectionView
基礎(chǔ)代碼就不貼了,想看的文章末尾有完整的Demo. 對
CollectionView
不熟悉的同學(xué)可以先去補(bǔ)補(bǔ)課,關(guān)于這方面的博客很多.
列一下核心代碼吧.
子類化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;
}
這個是關(guān)鍵方法,我們默認(rèn)只有一個section
.這個方法需要返回rect
范圍之內(nèi)的cell
的布局屬性,包括frame
與rect
相交的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;
// 因?yàn)槲覀兊腸ell有重疊,必須設(shè)置zIndex,否則復(fù)用時層級會有問題
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
這個時候看下效果:
第二步: 實(shí)現(xiàn)cell吸附在頂部的效果
這個時候我們就要控制滑動時cell的坐標(biāo),需要在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;
// 因?yàn)槲覀兊腸ell有重疊,必須設(shè)置zIndex,否則復(fù)用時層級會有問題
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可以重疊了
第三步: 實(shí)現(xiàn)下拉的彈簧效果
同樣是在上面的方法中增加代碼:
- (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的偏移比例,根據(jù)需要自行調(diào)節(jié)
cellRect.origin.y = att.indexPath.item * kCellSpace - fabs(offsetY + inset.top) + fabs(offsetY + inset.top) * att.indexPath.item * 0.25;
}
att.frame = cellRect;
// 因?yàn)槲覀兊腸ell有重疊,必須設(shè)置zIndex,否則復(fù)用時層級會有問題
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;
}
現(xiàn)在成這樣了:
第四步: 長按cell進(jìn)行移動
這部分的代碼比較復(fù)雜
首先給cell添加一個長按手勢,然后在FlowLayout
里面處理手勢的執(zhí)行! 然后對要移動的cell
進(jìn)行截圖,把這個ImageView
添加到當(dāng)前cell
上面,當(dāng)截圖進(jìn)行移動時,通過坐標(biāo)來判斷當(dāng)前cell
附近的cell
是否滿足交換條件,如果滿足交換條件,則調(diào)用- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath;
方法進(jìn)行交換,交換完成要更新層級!
另外,如果當(dāng)前cell
處于屏幕的底端或者頂端時,要讓CollectionView
進(jìn)行滾動!
因?yàn)轫敳康腸ell有吸附的效果,所以在進(jìn)行頂部滾動交換時會閃屏,沒有達(dá)到理想的交換效果,筆者暫時還無法解決,希望有大神可以指點(diǎn)下! 所以退而求其次,目前交換時會停止
CollectionView
的滾動.
下面是具體實(shí)現(xiàn):
#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
手勢的執(zhí)行方法:
#pragma mark -
#pragma mark -- -- -- -- -- - CollectionViewCell Delegate - -- -- -- -- --
- (void)collectionViewCell:(CollectionViewCell *)cell handlerLongPressGesture:(UILongPressGestureRecognizer *)ges {
switch (ges.state) {
case UIGestureRecognizerStateBegan:
{
// 對cell進(jìn)行截圖
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;
// 如果滑到頭則進(jìn)行滾動
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) {
// 根據(jù)第一次的手勢來判斷執(zhí)行那種滾動
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;
}
// 處于頂部或者底部的滾動范圍之內(nèi)不做處理
if (fabs(top) > kTopAutoScrollDistance && fabs(bottom) > kBottomAutoScrollDistance) {
[self handlerMoveItemAction];
}
} else {
// 滾動中則只根據(jù)距離來判斷
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;
}
}
計時器執(zhí)行方法:
#pragma mark -
#pragma mark -- -- -- -- -- - Event Response - -- -- -- -- --
- (void)displayLinkAction:(CADisplayLink *)link {
// 滾倒底了已經(jīng)
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: 優(yōu)化頂部的交換
[self handlerMoveItemAction];
}
執(zhí)行交換
- (void)handlerMoveItemAction {
if (!self.isMoveing) {
// 取到當(dāng)前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交換數(shù)據(jù)源
[self.delagate collectionViewFlowLayout:self moveItemAtIndexPath:self.currentIndexPath toIndexPath:preferIndexPath];
}
// 完成之后更新transform
self.shootImageView.layer.transform = CATransform3DMakeTranslation(0, 0, preferIndexPath.row * 2 + 1);
// 這個地方需要重新設(shè)置層級,更改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進(jìn)行截圖:
- (UIImage *)screenshotWithView:(UIView *)view {
UIGraphicsBeginImageContextWithOptions(view.frame.size, NO, [UIScreen mainScreen].scale);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
效果:
可以看到滾動時向下交換還是很流暢的,但是向上交換是第一個cel
l會閃一下!
第五步: 點(diǎn)擊cell打開
我們只需要重新設(shè)置可見范圍內(nèi)的cell
的UICollectionViewLayoutAttributes
,然后調(diào)用-(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的下標(biāo),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的偏移比例,根據(jù)需要自行調(diào)節(jié)
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);
// 因?yàn)槲覀兊腸ell有重疊,必須設(shè)置zIndex,否則復(fù)用時層級會有問題
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;
}
點(diǎn)擊展開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 {
// 結(jié)束
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) {
// 結(jié)束
[self closeCell];
} else {
// 復(fù)位
[UIView animateWithDuration:kComeUpAnimationDuration animations:^{
cell.frame = CGRectMake(0, offsetY, kCollectionViewWidth - inset.right, kCollectionViewHeight - kExpandBottomHeight);
}];
}
}
break;
default:
break;
}
}
效果如下:
但是展開的時候cell先全部靠左,再執(zhí)行動畫,比較奇怪,暫時還解決不掉,等想到了再更新吧!
文中如果有什么錯誤, 或者有什么好的想法,都可以留言! 我會更新!
轉(zhuǎn)載請注明出處!
http://www.lxweimin.com/p/b0fa1daa8665
GitHub:
https://github.com/TactBoy/ios-spring-shrink-move-expand-collectionView