UICollectionViewCell 长按重排
来源:互联网 发布:9月21网络大电影票房 编辑:程序博客网 时间:2024/05/29 18:01
首先,先祝博友们新年快乐!鸡年大吉! 在写这篇博文的时候,已经是1月25日,最后一天上班了,闲来无事,写下之前封装的拖动效果,希望能帮助有需要的博友们~
先上效果图。
1 、 全部 Cell 参与拖动效果。
2 、 禁止部分的 Cell 参与拖动。 (第一行的3个 cell 禁止)
3 、 跨 Cell 拖动。 (将一个 Cell 中的 UICollectionViewCell 拖动到另一个 Cell)
代码及类的简单说明。
1、 第一种拖动重排的方式
将Demo中的 UICollectionView+TLCellRearrange.h 拖到项目中,这是对 UICollectionView 做的一个扩展功能。
接口文件展示
#import <UIKit/UIKit.h>@protocol UICollectionViewCellRearrangeDelegate <NSObject>@optional- (BOOL)collectionView:(nonnull UICollectionView *)collectionView shouldMoveCell:(nonnull UICollectionViewCell *)cell atIndexPath:(nonnull NSIndexPath *)indexPath;- (void)collectionView:(nonnull UICollectionView *)collectionView putDownCell:(nonnull UICollectionViewCell *)cell atIndexPath:(nonnull NSIndexPath *)indexPath;- (BOOL)collectionView:(nonnull UICollectionView *)collectionView shouldMoveCell:(nonnull UICollectionViewCell *)cell fromIndexPath:(nonnull NSIndexPath *)fromIndexPath toIndexPath:(nonnull NSIndexPath *)toIndexPath;- (void)collectionView:(nonnull UICollectionView *)collectionView didMoveCell:(nonnull UICollectionViewCell *)cell fromIndexPath:(nonnull NSIndexPath *)fromIndexPath toIndexPath:(nonnull NSIndexPath *)toIndexPath;@end@interface UICollectionView (TLCellRearrange)@property (nonatomic, weak) id<UICollectionViewCellRearrangeDelegate> _Nullable rearrangeDelegate;@property (nonatomic, assign) BOOL enableRearrangement;/** * 长按cell接触边缘时collectionView自动滚动的速率,每1/60秒移动的距离 */@property (nonatomic, assign) CGFloat autoScrollSpeed;/** * 长按放大倍数 * 默认为1.2 */@property (nonatomic, assign) CGFloat longPressMagnificationFactor;@end
然后在使用的 VC 中, 创建 UICollectionView ,初始化之后,如下设置,加入扩展的委托 UICollectionViewCellRearrangeDelegate
- (void)setupUI { UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init]; [layout setScrollDirection:UICollectionViewScrollDirectionVertical]; [_collectionView setCollectionViewLayout:layout]; _collectionView.backgroundColor = [UIColor clearColor]; _collectionView.showsVerticalScrollIndicator = NO; _collectionView.alwaysBounceVertical = YES; [_collectionView registerClass:[WBAreaCollectionViewCell class] forCellWithReuseIdentifier:[WBAreaCollectionViewCell reuseIdentifier]]; _collectionView.rearrangeDelegate = self; _collectionView.enableRearrangement = YES; _collectionView.autoScrollSpeed = 3; _collectionView.longPressMagnificationFactor = 1.2; [self updateContent];}
这样就能实现拖动的重排效果了。 然后拖动后的回调在对应的委托方法里
#pragma mark - UICollectionViewCellRearrangeDelegate methods- (BOOL)collectionView:(UICollectionView *)collectionView shouldMoveCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0 || indexPath.row == 1 || indexPath.row == 2) { // 如果不需要参与 长按拖动 和 重排效果 的cell, 在这里判断返回NO 即可。 return NO; } NSLog(@"开始位置:%ld", indexPath.row); return YES;}- (void)collectionView:(UICollectionView *)collectionView putDownCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { // 最终停留在的 NSLog(@"最终到:%ld", indexPath.row);}- (BOOL)collectionView:(UICollectionView *)collectionView shouldMoveCell:(UICollectionViewCell *)cell fromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { id tempObject = self.areaModels[fromIndexPath.row]; [self.areaModels removeObjectAtIndex:fromIndexPath.row]; [self.areaModels insertObject:tempObject atIndex:toIndexPath.row]; return YES;}- (void)collectionView:(UICollectionView *)collectionView didMoveCell:(UICollectionViewCell *)cell fromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { // 途径的位置 NSLog(@"从:%ld, 到x:%ld", fromIndexPath.row, toIndexPath.row);}
2 、 禁止部分 Cell 参与重排的方式,只需要在对应的委托方法里设置返回 NO 即可, 如下 不想要 row = 0, 1, 2 的 Cell 参与重排。
- (BOOL)collectionView:(UICollectionView *)collectionView shouldMoveCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath { if (indexPath.row == 0 || indexPath.row == 1 || indexPath.row == 2) { // 如果不需要参与 长按拖动 和 重排效果 的cell, 在这里判断返回NO 即可。 return NO; } NSLog(@"开始位置:%ld", indexPath.row); return YES;}
3 、 跨 Cell 拖动重排。 这里主要讲下实现的思路,有了思路才能更好的自由扩展成自己的需要。
我们先看下最内部 设备 Cell 的接口文件。
#import <UIKit/UIKit.h>@class WBDeviceModel;@interface WBDeviceCollectionViewCell : UICollectionViewCell+ (NSString *)reuseIdentifier;- (void)setupWithModel:(WBDeviceModel *)deviceModel;@property (nonatomic, assign) BOOL isDisableLongPress; // 是否禁用自定义长按手势@property (nonatomic, assign) BOOL isSelect;@property (nonatomic, copy) void (^longPressDragBeganBlock)(CGPoint centerPoint);// 长按开始@property (nonatomic, copy) void (^longPressDragChangeBlock)(CGPoint centerPoint);// 长按拖动过程@property (nonatomic, copy) void (^longPressDragEndedBlock)(WBDeviceModel *currentDeviceModel, CGPoint centerPoint);// 长按结束@end
红色部分是我们自己提供出来的 长按手势 的过程接口,开始,过程和结束都是长按手势的一些基本阶段,我们可以按照自己的需要修改接口。
内部的实现,在长按的开始,将长按的 Cell 绘制成快照 self.snapshotView ,并且放大1.1倍 ,然后计算当前处于 Window 上的Frame,并且添加在 Window 上。
然后在拖动过程中,实时的计算基于 window 的位置,并且更新 Frame,并且实时回调最新的 Frame ( 如果你需要这个需求的话) 。
在拖动结束时,可以回调结束时的Frame ,以及你选中的数据。
因为这里的 UI 是有两层 Cell 之后才回调到 VC 的, WBDeviceCollectionViewCell 的数据是回调到了 WBAreaDetaliCollectionViewCell 中, WBAreaDetaliCollectionViewCell 再回调到 WBAreaDetailVC 。
#pragma mark - UIGestureRecognizer- (void)selfContentViewLongPressed:(UILongPressGestureRecognizer *)sender { UIView *view = (UIView *)sender.view; UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; if (sender.state == UIGestureRecognizerStateBegan) { _startPoint = [sender locationInView:sender.view]; _originPoint = view.center; // iphone7及以上 或者 模拟器,自己绘制快照 if ([[GetDeviceInfo() getCurrentDeviceModel] isEqualToString:@"iPhone7"] || [[GetDeviceInfo() getCurrentDeviceModel] isEqualToString:@"iPhone7Plus"] || [[GetDeviceInfo() getCurrentDeviceModel] isEqualToString:@"iPhoneSimulator"] ) { self.snapshotView = [[UIImageView alloc] initWithImage:[UIImage imageFromView:view]]; } else { self.snapshotView = [view snapshotViewAfterScreenUpdates:NO]; } // 添加在 window 上 [window addSubview:self.snapshotView]; CGRect toWindowFrame = [view convertRect:view.bounds toView:window]; self.snapshotView.frame = toWindowFrame; [UIView animateWithDuration:Duration animations:^{ // 放大1.1倍 self.snapshotView.transform = CGAffineTransformMakeScale(1.1, 1.1); self.snapshotView.alpha = 0.9; }]; BLOCK_SAFE_CALLS(self.longPressDragBeganBlock, [self fetchCenterPointWithRect:toWindowFrame]); } else if (sender.state == UIGestureRecognizerStateChanged) { CGPoint newPoint = [sender locationInView:sender.view]; CGFloat deltaX = newPoint.x - _startPoint.x; CGFloat deltaY = newPoint.y - _startPoint.y; view.center = CGPointMake(view.center.x + deltaX, view.center.y + deltaY); CGRect toWindowFrame = [view convertRect:view.bounds toView:window]; self.snapshotView.frame = toWindowFrame; BLOCK_SAFE_CALLS(self.longPressDragChangeBlock, [self fetchCenterPointWithRect:toWindowFrame]); } else if (sender.state == UIGestureRecognizerStateEnded) { [UIView animateWithDuration:Duration animations:^{ self.snapshotView.transform = CGAffineTransformIdentity; self.snapshotView.alpha = 1.0; }]; [self.snapshotView removeFromSuperview]; self.snapshotView = nil; CGRect toWindowFrame = [view convertRect:view.bounds toView:window]; BLOCK_SAFE_CALLS(self.longPressDragEndedBlock, self.currentDeviceModel, [self fetchCenterPointWithRect:toWindowFrame]); }}
回调到 VC 之后,我们要做一种状态, 就是拖动到某个 Cell 上的响应状态, Demo 中是将背景色 更改为 浅白色 。
我们 Cell 返回的 centerPoint是自身中心点基于 window 的位置,然后就有下面这个方法, 利用这个返回的 centerPoint判断一个点是否在某一个区域内,即 是否在某个 Cell 上, 用中心点类判断,是为了确定这个Cell拖动的某个Cell上的唯一性,故不能用Cell的其他四点作为判断点。
// 选中项更新高亮- (void)judgeDragPositionWithCenterPoint:(CGPoint)centerPoint indexPath:(NSIndexPath *)indexPath { NSArray<NSIndexPath *> *indexPaths = [self.collectionView indexPathsForVisibleItems]; UIWindow *window = [[[UIApplication sharedApplication] delegate] window]; [indexPaths enumerateObjectsUsingBlock:^(NSIndexPath * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { if (indexPath.row != obj.row) { WBAreaDetaliCollectionViewCell *cell = (WBAreaDetaliCollectionViewCell *)[self.collectionView cellForItemAtIndexPath:obj]; CGRect toWindowFrame = [cell convertRect:cell.bounds toView:window]; cell.isDragCurrent = CGRectContainsPoint(toWindowFrame, centerPoint) ? YES : NO; } }];}
VC 上,Cell 的回调情况如下。
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { WEAK_SELF(); WBAreaDetaliCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[WBAreaDetaliCollectionViewCell reuseIdentifier] forIndexPath:indexPath]; // 初始化重用处理 *** [cell setupWithModel:self.areasDetail[indexPath.row]]; // 内部collectionView的offset [cell setupCollectionViewOffsetWitnPointValue:self.pointsValue[INT_TO_STRING(indexPath.row)]]; // *** // 长按拖动效果 *** cell.isDragCurrent = NO; [cell setPointChangeBlock:^(NSValue *pointValue) { [weakSelf.pointsValue setObject:pointValue forKey:INT_TO_STRING(indexPath.row)]; }]; [cell setLongPressDragBeganBlock:^(CGPoint centerPoint) { [weakSelf judgeDragPositionWithCenterPoint:centerPoint indexPath:indexPath]; }]; [cell setLongPressDragChangeBlock:^(CGPoint centerPoint) { [weakSelf judgeDragPositionWithCenterPoint:centerPoint indexPath:indexPath]; }]; [cell setLongPressDragEndedBlock:^(WBAreaDetailModel *currentAreaDetailModel, WBDeviceModel *currentDeviceModel, CGPoint centerPoint) { // 先判断一下拖动到的位置是在哪个 IndexPath 上 (可能为空) NSIndexPath *selectedIndexPath = [weakSelf returnSelectedItemWithCenterPoint:centerPoint indexPath:indexPath]; [weakSelf.collectionView reloadItemsAtIndexPaths:@[ indexPath ]]; if (!selectedIndexPath) { return ; } else { /* * selectedAreaDetailModel 将要移动到的位置 * currentAreaDetailModel 当前数据源的位置 * currentDeviceModel 当前设备数据 Model */ // 调用自己的重排 API WBAreaDetailModel *selectedAreaDetailModel = self.areasDetail[selectedIndexPath.row]; NSString *str = [NSString stringWithFormat:@"%@ - %@\n\n移动到\n\n%@", currentAreaDetailModel.roomName, currentDeviceModel.deviceName, selectedAreaDetailModel.roomName]; ShowTips(str); } }]; return cell;}
CSDN下载
1 0
- UICollectionViewCell 长按重排
- UIcollectionViewCell(UItableviewcell)长按删除操作
- Objective—C实现UICollectionView中UICollectionViewCell的重排实现
- 解决iOS中UICollectionViewCell点击无反应,长按才能选中的bug
- UICollectionViewCell
- UICollectionViewCell
- UICollectionViewCell 布局
- UICollectionViewCell复用
- 移动UICollectionViewCell
- 长按
- UICollectionviewCell自适应label长度
- UICollectionViewCell的使用
- 设置UICollectionViewcell无间隙
- 关于UICollectionViewCell的问题
- UICollectionView 及 UICollectionViewCell 复用
- iOS 自定义UICollectionViewCell
- UICollectionViewCell的indexPath问题
- 设置UICollectionViewCell选中颜色
- 原生js格式化json和格式化xml的方法
- 一个字母引发的血案 java.io.File中mkdir()和mkdirs()
- 基于用户的协同过滤算法和基于物品的协同过滤算法之java实现
- 实现MD5加密的代码
- 七、带你进入神奇的symfony世界
- UICollectionViewCell 长按重排
- 使用SourceTree出现 git -c diff.mnemonicprefix=false -c core.quotepath=false
- GIT使用说明
- day13:JavaScript DOM编程学习笔记05
- 随机产生数字加字母的一组字符串
- 什么是SEO?
- Node.js+express+MySQL使用七牛云实现的用户头像修改
- Android CheckBox 多选按钮
- 机器学习