该功能的效果图如下
下面给出主要代码,文字末尾有demo下载地址
一、获取数据源模型
此处数据源只为演示使用,是固定的,先创建一个数据源模型
#import <UIKit/UIKit.h>
@interface dataModel : NSObject
@property (nonatomic,strong) NSString *imageName;
@property (nonatomic,strong) NSString *appName;
+(instancetype)initWithDict:(NSDictionary *)dict;
@end
在dataModel.m文件中实现其方法
#import "dataModel.h"
@implementation dataModel
- (instancetype)initWith:(NSDictionary *)dict {
self = [super init];
if (self) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
+(instancetype)initWithDict:(NSDictionary *)dict{
return [[self alloc] initWith:dict];
}
二、在ViewController.m文件中把数据转模型
for (NSDictionary *dict in dataArr) {
dataModel *model = [dataModel initWithDict:dict];
[_viewModels addObject:model];
}
获取到的数据源存到_viewModels数组中
三、设置UI界面
主要是设置UICollectionView,此处是直接在storyboard上添加的控件,但是还需要设置layout属性
self.automaticallyAdjustsScrollViewInsets = NO;
_collectionView.backgroundColor = RGB(238, 238, 244);
_collectionView.alwaysBounceVertical = YES;
UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
CGFloat itemH = ([UIScreen mainScreen].bounds.size.width - 3 * itemMargin - 2 * leftMargin) / 4;
layout.itemSize = CGSizeMake(itemH, itemH);
layout.minimumLineSpacing = itemMargin;
layout.minimumInteritemSpacing = itemMargin;
_collectionView.collectionViewLayout = layout;
_longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressMoving:)];
_longPress.minimumPressDuration = 1.0;
[_collectionView addGestureRecognizer:_longPress];
// 如果使用storyboard来加载cell就不要在注册了,否则会调用initWithFrame方法,重新加载一遍
[_collectionView registerClass:[AppCollectionViewCell class] forCellWithReuseIdentifier:identifier];
//_collectionView.dataSource = self;
_collectionView.contentInset = UIEdgeInsetsMake(leftMargin, leftMargin, 0, leftMargin);
上述代码主要是这是了_collectionView的collectionViewLayout属性,可以根据自己需要调整cell大小,最后给_collectionView添加长按手势,用来进入编辑状态
四、重点部分,长按进入编辑界面的操作
- (void)longPressMoving:(UILongPressGestureRecognizer *)longPress {
switch (longPress.state) {
case UIGestureRecognizerStateBegan:
{
_originalIndexPath = [_collectionView indexPathForItemAtPoint:[longPress locationInView:_collectionView]];
if (_originalIndexPath.row > _viewModels.count) {
return;
}
if (!isEditing) {
[self enterEditingModel];
}
//获取到手指所在cell
AppCollectionViewCell *cell = (AppCollectionViewCell *)[_collectionView cellForItemAtIndexPath:_originalIndexPath];
UIImage *image = cell.appImageView.image;
NSString *name = cell.nameLabel.text;
//重新生成一个和cell一样的view
UIView *view1 = [self viewFromCell:cell image:image andName:name];
// 当前view绘制成图片
UIImage *image1 = [self imageFromView:view1];
UIImageView *snapView = [[UIImageView alloc] initWithImage:image1];
_tempMoveCell = snapView;
_tempMoveCell.frame = cell.frame;
cell.hidden = YES;
[_collectionView addSubview:_tempMoveCell];
_lastPoint = [longPress locationOfTouch:0 inView:longPress.view];
}
break;
case UIGestureRecognizerStateChanged:
{
CGFloat tranX = [longPress locationOfTouch:0 inView:longPress.view].x - _lastPoint.x;
CGFloat tranY = [longPress locationOfTouch:0 inView:longPress.view].y - _lastPoint.y;
_tempMoveCell.center = CGPointApplyAffineTransform(_tempMoveCell.center, CGAffineTransformMakeTranslation(tranX, tranY));
_lastPoint = [longPress locationOfTouch:0 inView:longPress.view];
[self moveCell];
}
break;
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
{
if (_originalIndexPath.row > _viewModels.count) {
return;
}
AppCollectionViewCell *cell = (AppCollectionViewCell *)[_collectionView cellForItemAtIndexPath:_originalIndexPath];
_collectionView.userInteractionEnabled = NO;
cell.alpha = 0.0;
[UIView animateWithDuration:0.25 animations:^{
_tempMoveCell.center = cell.center;
_tempMoveCell.alpha = 0.0;
cell.alpha = 1.0;
cell.hidden = NO;
} completion:^(BOOL finished) {
[_tempMoveCell removeFromSuperview];
_originalIndexPath = nil;
_tempMoveCell = nil;
_collectionView.userInteractionEnabled = YES;
}];
}
break;
default:
break;
}
}
该方法是cell长按开始,移动和结束时的状态监听
当cell移动时,需要计算当前cell与另其他cell的中心距
并且移动之后需要更新数据源
- (void)moveCell{
for (AppCollectionViewCell *cell in [_collectionView visibleCells]) {
NSIndexPath *index = [_collectionView indexPathForCell:cell];
if (index == _originalIndexPath) {
continue;
}
//计算中心距
CGFloat spacingX = fabs(_tempMoveCell.center.x - cell.center.x);
CGFloat spacingY = fabs(_tempMoveCell.center.y - cell.center.y);
if (spacingX <= _tempMoveCell.bounds.size.width / 2.0f && spacingY <= _tempMoveCell.bounds.size.height / 2.0f) {
_moveIndexPath = [_collectionView indexPathForCell:cell];
if (_moveIndexPath.row<_viewModels.count) { //超出cell范围时移动会崩溃
//更新数据源
[self updateDataSource];
//移动
[_collectionView moveItemAtIndexPath:_originalIndexPath toIndexPath:_moveIndexPath];
//设置移动后的起始indexPath
_originalIndexPath = _moveIndexPath;
}
break;
}
}
}
//更新数据源
- (void)updateDataSource{
NSMutableArray *temp = @[].mutableCopy;
[temp addObjectsFromArray:_viewModels];
if (_moveIndexPath.item > _originalIndexPath.item) {
for (NSUInteger i = _originalIndexPath.item; i < _moveIndexPath.item ; i ++) {
[temp exchangeObjectAtIndex:i withObjectAtIndex:i + 1];
}
}else{
for (NSUInteger i = _originalIndexPath.item; i > _moveIndexPath.item ; i --) {
[temp exchangeObjectAtIndex:i withObjectAtIndex:i - 1];
}
}
_viewModels = temp.mutableCopy;
}
最后点击完成可以退出编辑状态,点击添加可以添加应用。最后附上完整demo地址