简单写了一个collectionview的拖拽效果,记录下.只是简单的实现了拖拽,其它都没有加
实现思路:
- 首先给
collectionview
添加长按的手势 - 找到当前长按的
cell
, 截图保存为一个view(实际拖拽过程中是拖拽的这个view) ,然后隐藏原先的cell
- 拖拽截图的
view
,在拖拽的过程中和collectionview
上已有的cell
进行对比,交换位置,改变数据 - 拖拽结束之后,删除截图的
view
,显示最开始的cell
具体代码
- 创建一个继承
UICollectionViewFlowLayout
的类,你也可以直接写在viewcontroller
中.(名字随便写的)
#import <UIKit/UIKit.h>
@protocol DFZFBLayoutDelegate <NSObject>
//去改变数据源
- (void)moveDataItem:(NSIndexPath*)fromIndexPath toIndexPath:(NSIndexPath*)toIndexPath;
@end
@interface DFZFBLayout : UICollectionViewFlowLayout
@property (nonatomic, assign) id<DFZFBLayoutDelegate> delegate;
@end
- .m中具体的实现
- 这里只是简单的添加一个手势去做的,会慢慢改进.
#import "DFZFBLayout.h"
@interface DFZFBLayout ()<UIGestureRecognizerDelegate>
@property (nonatomic, strong) UILongPressGestureRecognizer *longPress;
@property (nonatomic, strong) NSIndexPath *currentIndexPath; //当前的IndexPath
@property (nonatomic, strong) UIView *mappingImageCell; //拖动cell的截图
@end
@implementation DFZFBLayout
- (id)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
if (self) {
[self configureObserver];
}
return self;
}
- (instancetype)init{
self = [super init];
if (self) {
[self configureObserver];
}
return self;
}
- (void)dealloc{
[self removeObserver:self forKeyPath:@"collectionView"];
}
#pragma mark - setup
- (void)configureObserver{
[self addObserver:self forKeyPath:@"collectionView" options:NSKeyValueObservingOptionNew context:nil];
}
- (void)setUpGestureRecognizers{
if (self.collectionView == nil) {
return;
}
_longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(handleLongPress:)];
_longPress.minimumPressDuration = 0.2f;
_longPress.delegate = self;
[self.collectionView addGestureRecognizer:_longPress];
}
#pragma mark - observer
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
if ([keyPath isEqualToString:@"collectionView"]) {
[self setUpGestureRecognizers];
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)handleLongPress:(UILongPressGestureRecognizer*)longPress
{
switch (longPress.state) {
case UIGestureRecognizerStateBegan:
{
CGPoint location = [longPress locationInView:self.collectionView];
NSIndexPath* indexPath = [self.collectionView indexPathForItemAtPoint:location];
if (!indexPath) return;
self.currentIndexPath = indexPath;
UICollectionViewCell* targetCell = [self.collectionView cellForItemAtIndexPath:self.currentIndexPath];
//得到当前cell的映射(截图)
UIView* cellView = [targetCell snapshotViewAfterScreenUpdates:YES];
self.mappingImageCell = cellView;
self.mappingImageCell.frame = cellView.frame;
targetCell.hidden = YES;
[self.collectionView addSubview:self.mappingImageCell];
cellView.center = targetCell.center;
}
break;
case UIGestureRecognizerStateChanged:
{
CGPoint point = [longPress locationInView:self.collectionView];
//更新cell的位置
self.mappingImageCell.center = point;
NSIndexPath * indexPath = [self.collectionView indexPathForItemAtPoint:point];
if (indexPath == nil ) return;
if (![indexPath isEqual:self.currentIndexPath])
{
//改变数据源
if ([self.delegate respondsToSelector:@selector(moveDataItem:toIndexPath:)]) {
[self.delegate moveDataItem:self.currentIndexPath toIndexPath:indexPath];
}
[self.collectionView moveItemAtIndexPath:self.currentIndexPath toIndexPath:indexPath];
self.currentIndexPath = indexPath;
}
}
break;
case UIGestureRecognizerStateEnded:
{
UICollectionViewCell *cell = [self.collectionView cellForItemAtIndexPath:self.currentIndexPath];
[UIView animateWithDuration:0.25 animations:^{
self.mappingImageCell.center = cell.center;
} completion:^(BOOL finished) {
[self.mappingImageCell removeFromSuperview];
cell.hidden = NO;
self.mappingImageCell = nil;
self.currentIndexPath = nil;
}];
}
break;
default:
{
}
break;
}
}
@end
应用
- 在viewcontroller中代码:
DFZFBLayout* layout = [[DFZFBLayout alloc]init];
layout.delegate = self;
//layout.dataSource = self;
layout.minimumInteritemSpacing = 1;
layout.minimumLineSpacing = 1;
layout.sectionInset = UIEdgeInsetsMake(1, 1, 1, 1);
layout.headerReferenceSize = CGSizeMake(SCREEN_WIDTH, 40.f);
// layout.footerReferenceSize = CGSizeMake(SCREEN_WIDTH, 0.f);
// [layout setSectionInset:UIEdgeInsetsMake(0, 0,20,0)];
myCollectionView = [[UICollectionView alloc]initWithFrame:CGRectZero collectionViewLayout:layout];
myCollectionView.delegate = self;
myCollectionView.dataSource = self;
myCollectionView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:myCollectionView];
效果
iOS9之后 提供了API 记录下,方便以后使用
// Support for reordering
- (BOOL)beginInteractiveMovementForItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0); // returns NO if reordering was prevented from beginning - otherwise YES
- (void)updateInteractiveMovementTargetPosition:(CGPoint)targetPosition NS_AVAILABLE_IOS(9_0);
- (void)endInteractiveMovement NS_AVAILABLE_IOS(9_0);
- (void)cancelInteractiveMovement NS_AVAILABLE_IOS(9_0);
- (void)handlelongGesture:(UILongPressGestureRecognizer *)longGesture {
//判断手势状态
switch (longGesture.state) {
case UIGestureRecognizerStateBegan:{
//判断手势落点位置是否在路径上
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:[longGesture locationInView:self.collectionView]];
if (indexPath == nil) {
break;
}
//在路径上则开始移动该路径上的cell
[self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];
}
break;
case UIGestureRecognizerStateChanged:
//移动过程当中随时更新cell位置
[self.collectionView updateInteractiveMovementTargetPosition:[longGesture locationInView:self.collectionView]];
break;
case UIGestureRecognizerStateEnded:
//移动结束后关闭cell移动
[self.collectionView endInteractiveMovement];
break;
default:
[self.collectionView cancelInteractiveMovement];
break;
}
}
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath{
//返回YES允许其item移动
return YES;
}
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath {
//取出源item数据
id objc = [_dataSource objectAtIndex:sourceIndexPath.item];
//从资源数组中移除该数据
[_dataSource removeObject:objc];
//将数据插入到资源数组中的目标位置上
[_dataSource insertObject:objc atIndex:destinationIndexPath.item];
}