效果图如下
原理
- 给每一个UICollectionViewCell添加一个长按手势
- 当长按cell时, 截取该cell, 创建一模一样的UIImageView
- 将创建的UIImageView添加到UICollectionView上, 调整位置和被点击的UICollectionViewCell重合, 然后隐藏被点击的UICollectionViewCell
- 手指移动时, 获取手势在UICollectionView上的位置, 并移动创建的UIImageView的中心点与手指位置重合
- 循环遍历所有界面所有的cell, 并计算UIimageView与这些cell中心点的距离
- 获取到中心点距离后, 写一个判断语句, 判断cell移动的时机, 这里是<a>中心点距离 < cell宽度一半 并且 两个cell高度距离差值为 cell高度一半时</a> 进行移动cell
- 将被点击的cell 所对应的数据从数据源中删除, 在插入到需要移动到的位置
- 调用UICollectionView移动cell的方法进行移动
- 注意: 移动的时机可以根据需求而定, 整体就是通过<a>截图, 移动, 移动数据源, 移动cell的方式</a>进行
UICollectionView中的代码
#import "LTViewController.h"
#import "LTCollectionViewCell.h"
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height
@interface LTViewController () <UICollectionViewDataSource, LTCollectionViewCellDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
/** 数据源 */
@property (nonatomic, strong) NSMutableArray *dataArr;
@end
@implementation LTViewController
static NSString *LTCollectionViewId = @"LTCollectionViewCell";
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat width = ([UIScreen mainScreen].bounds.size.width - 50) / 4.0;
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)[self.collectionView collectionViewLayout];
layout.itemSize = CGSizeMake(width, width);
layout.minimumLineSpacing = 10;
layout.minimumInteritemSpacing = 10;
layout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10);
[self.collectionView registerNib:[UINib nibWithNibName:NSStringFromClass([LTCollectionViewCell class]) bundle:nil] forCellWithReuseIdentifier:LTCollectionViewId];
self.navigationItem.title = @"长按cell移动";
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSMutableArray *)dataArr
{
if (!_dataArr) {
self.dataArr = [@[@"推荐",@"视频",@"军事",@"娱乐",@"问答",@"娱乐",@"汽车",@"段子",@"趣图",@"财经",@"热点",@"房产",@"社会",@"数码",@"美女",@"数码",@"文化",@"美文",@"星座",@"旅游",@"视频",@"军事",@"娱乐",@"问答",@"娱乐",@"汽车",@"段子",@"趣图",@"财经",@"热点",@"房产",@"社会",@"数码",@"美女",@"数码",@"文化",@"美文",@"星座",@"旅游"] mutableCopy];
}
return _dataArr;
}
#pragma mark - <UICollectionViewDataSource>
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.dataArr.count;
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
LTCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:LTCollectionViewId forIndexPath:indexPath];
cell.title = self.dataArr[indexPath.row];
cell.delegate = self;
return cell;
}
#pragma mark - <handler action>
/*
长按手势响应事件
*/
- (void)longPressClick:(UILongPressGestureRecognizer *)gesture
{
// 记录手指的位置
static CGPoint point;
// 截取的图片
static UIImageView *view;
// 长按手势开始时的操作
if (gesture.state == UIGestureRecognizerStateBegan) {
// 截图操作
view = [self imageViewWithCell:(LTCollectionViewCell *)gesture.view];
// 隐藏被点击的cell
gesture.view.hidden = YES;
// 截图的位置 与 被点击的cell重合
view.center = gesture.view.center;
// 添加截图到UICollectionView
[self.collectionView addSubview:view];
// 记录当前手指位置
point = [gesture locationOfTouch:0 inView:self.collectionView];
}
// 手指移动时的操作
if (gesture.state == UIGestureRecognizerStateChanged) {
CGFloat x = [gesture locationOfTouch:0 inView:self.collectionView].x - point.x;
CGFloat y = [gesture locationOfTouch:0 inView:self.collectionView].y - point.y;
// 移动截图
view.center = CGPointApplyAffineTransform(view.center, CGAffineTransformMakeTranslation(x, y));
point = [gesture locationOfTouch:0 inView:self.collectionView];
// 循环获取当前界面所有的cell, 并进行判断操作
for (LTCollectionViewCell *cell in [self.collectionView visibleCells]) {
if (cell.hidden == YES) continue;
// 中心点距离
CGFloat space = sqrtf(pow(cell.center.x - view.center.x, 2) + pow(cell.center.y - view.center.y, 2));
// 移动cell的时机判断
if (space <= view.frame.size.width * 0.5 && space < fabs(view.center.y - cell.center.y) < cell.frame.size.height * 0.5) {
// cell移动的起点位置
NSIndexPath *formIndexPath = [self.collectionView indexPathForCell:(LTCollectionViewCell *)gesture.view];
// cell移动的终点位置
NSIndexPath *toIndexPath = [self.collectionView indexPathForCell:cell];
// 取出被点击cell的数据
NSString *string = [self.dataArr objectAtIndex:formIndexPath.item];;
// 数据源删除被点击的cell数据
[self.dataArr removeObjectAtIndex:formIndexPath.item];
// 将取出的数据 插入到 需要移动到的位置
[self.dataArr insertObject:string atIndex:toIndexPath.item];
// 移动cell
[self.collectionView moveItemAtIndexPath:formIndexPath toIndexPath:toIndexPath];
break;
}
}
}
// 手指离开屏幕时的操作, 移除截图, 显示cell
if (gesture.state == UIGestureRecognizerStateEnded) {
[UIView animateWithDuration:0.25 animations:^{
view.center = gesture.view.center;
view.backgroundColor = gesture.view.backgroundColor;
} completion:^(BOOL finished) {
[view removeFromSuperview];
gesture.view.hidden = NO;
}];
}
}
/*
截取被点击cell生成图片
*/
- (UIImageView *)imageViewWithCell:(LTCollectionViewCell *)cell
{
//1. 开启位图上下文
UIGraphicsBeginImageContextWithOptions(cell.frame.size, NO, 0.0);
//2. 将屏幕绘制到上下文中
CGContextRef content = UIGraphicsGetCurrentContext();
[cell.layer renderInContext:content];
//3. 从上下文中获取绘制的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//4. 关闭上下文
UIGraphicsEndImageContext();
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3];
imageView.layer.cornerRadius = cell.layer.cornerRadius;
imageView.layer.masksToBounds = cell.layer.masksToBounds;
return imageView;
}
@end
UICollectionViewCell中的代码
#import <UIKit/UIKit.h>
@protocol LTCollectionViewCellDelegate <NSObject>
@optional;
- (void)longPressClick:(UILongPressGestureRecognizer *)gesture;
@end
@interface LTCollectionViewCell : UICollectionViewCell
/** 标题 */
@property (nonatomic, copy) NSString *title;
/** 代理 */
@property (nonatomic, weak) id<LTCollectionViewCellDelegate> delegate;
@end
#import "LTCollectionViewCell.h"
@interface LTCollectionViewCell ()
@property (weak, nonatomic) IBOutlet UILabel *label;
@end
@implementation LTCollectionViewCell
- (void)awakeFromNib {
[super awakeFromNib];
self.layer.cornerRadius = 5;
self.layer.masksToBounds = YES;
self.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.layer.borderWidth = 1;
[self addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressClick:)]];
}
- (void)setTitle:(NSString *)title
{
_title = title;
self.label.text = title;
}
- (void)longPressClick:(UILongPressGestureRecognizer *)gesture
{
if ([self.delegate respondsToSelector:@selector(longPressClick:)]) {
[self.delegate longPressClick:gesture];
}
}
@end