效果图:

github下载地址:DHDragableCellTableView

使用

将tableView继承与DHDragableCellTableView并遵循协议DHDragableCellTableViewDataSource,DHDragableCellTableViewDelegate

#pragma mark - DHDragableCellTableViewDataSource- (NSArray *)dataSourceArrayInTableView:(DHDragableCellTableView *)tableView{    return self.dataSource.copy;//数据源}

- (void)tableView:(DHDragableCellTableView *)tableView newDataSourceArrayAfterMove:(NSArray *)newDataSourceArray{    self.dataSource = newDataSourceArray.mutableCopy;//返回的数据源    [self.tableView reloadData];}复制代码

实现:

大概思路,为UITableView添加长按手势,长按后给选择的cell截图并隐藏选择的cell,让截图跟随手势移动

1.添加手势

/** 添加手势 */- (void)dh_addGesture{    _gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(dh_processGesture:)];    _gesture.minimumPressDuration = _gestureMinimumPressDuration;    [self addGestureRecognizer:_gesture];}复制代码

2.监听手势状态

- (void)dh_processGesture:(UILongPressGestureRecognizer *)gesture{    switch (gesture.state) {        case UIGestureRecognizerStateBegan:        {            [self dh_gestureBegan:gesture];        }            break;        case UIGestureRecognizerStateChanged:        {            if (!_canEdgeScroll) {                [self dh_gestureChanged:gesture];            }        }            break;        case UIGestureRecognizerStateEnded:        case UIGestureRecognizerStateCancelled:        {            [self dh_gestureEndedOrCancelled:gesture];        }            break;        default:            break;    }}复制代码

3.开始拖动

- (void)dh_gestureBegan:(UILongPressGestureRecognizer *)gesture{    CGPoint point = [gesture locationInView:gesture.view];    self.lastPoint = point;    NSIndexPath *selectedIndexPath = [self indexPathForRowAtPoint:point];    if (!selectedIndexPath) {        return;    }    if (self.delegate && [self.delegate respondsToSelector:@selector(tableView:willMoveCellAtIndexPath:)]) {        [self.delegate tableView:self willMoveCellAtIndexPath:selectedIndexPath];    }    if (_canEdgeScroll) {        //开启边缘滚动        [self dh_startEdgeScroll];    }    //每次移动开始获取一次数据源    if (self.dataSource && [self.dataSource respondsToSelector:@selector(dataSourceArrayInTableView:)]) {        _tempDataSource = [self.dataSource dataSourceArrayInTableView:self].mutableCopy;    }    _selectedIndexPath = selectedIndexPath;    UITableViewCell *cell = [self cellForRowAtIndexPath:selectedIndexPath];    _tempView = [self dh_snapshotViewWithInputView:cell];    if (_drawMovalbeCellBlock) {        //将_tempView通过block让使用者自定义        _drawMovalbeCellBlock(_tempView);    }else {        //配置默认样式        _tempView.layer.shadowColor = [UIColor grayColor].CGColor;        _tempView.layer.masksToBounds = NO;        _tempView.layer.cornerRadius = 0;        _tempView.layer.shadowOffset = CGSizeMake(-5, 0);        _tempView.layer.shadowOpacity = 0.4;        _tempView.layer.shadowRadius = 5;    }    _tempView.frame = cell.frame;    [self addSubview:_tempView];    //隐藏cell    cell.hidden = YES;    [UIView animateWithDuration:kDH_DragableCellAnimationTime animations:^{        _tempView.center = CGPointMake(_tempView.center.x, point.y);    }];}复制代码

4.拖动 这里的_toBottom是int类型用来判断手势是向哪一个方向拖动,然后根据拖动的cell跟要交换的cell的中心点进行比较,判断是否交换

- (void)dh_gestureChanged:(UILongPressGestureRecognizer *)gesture{    CGPoint point = [gesture locationInView:gesture.view];    //判断拖动的方向    if (point.y - self.lastPoint.y > 0) {        _toBottom = 1;//向下拖    }else if(point.y - self.lastPoint.y < 0){        _toBottom = -1;//向上拖    }else{        _toBottom = 0;    }    self.lastPoint = point;    NSIndexPath *currentIndexPath = [self indexPathForRowAtPoint:point];    if (currentIndexPath && ![_selectedIndexPath isEqual:currentIndexPath]) {        UITableViewCell *cell = [self cellForRowAtIndexPath:_selectedIndexPath];        UITableViewCell *cell1 = [self cellForRowAtIndexPath:currentIndexPath];        //将拖动的cell跟要交换的cell的centerY进行比较        if ((_toBottom == 1 && (point.y+cell.frame.size.height/2) >= CGRectGetMaxY(cell1.frame) && (CGRectGetMaxY(cell1.frame) >= CGRectGetMaxY(cell.frame))) || ((_toBottom == -1 && (point.y-cell.frame.size.height/2) <= CGRectGetMinY(cell1.frame)) && (CGRectGetMinY(cell1.frame) <= CGRectGetMinY(cell.frame)))) {            //交换数据源和cell            [self dh_updateDataSourceAndCellFromIndexPath:_selectedIndexPath toIndexPath:currentIndexPath];            if (self.delegate && [self.delegate respondsToSelector:@selector(tableView:didMoveCellFromIndexPath:toIndexPath:)]) {                [self.delegate tableView:self didMoveCellFromIndexPath:_selectedIndexPath toIndexPath:currentIndexPath];            }            _selectedIndexPath = currentIndexPath;        }    }    //让截图跟随手势    _tempView.center = CGPointMake(_tempView.center.x, point.y);}复制代码

5.交换数据跟cell的位置 为了在不同行高交换时cell不变形,交换后要立刻reloadData,再通过对两个cell截图,用截图来模拟交换的动画

/**交换数据源 跟 cell的位置*/
- (void)dh_updateDataSourceAndCellFromIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{if ([self numberOfSections] == 1) {//只有一组[_tempDataSource exchangeObjectAtIndex:fromIndexPath.row withObjectAtIndex:toIndexPath.row];//交换cell[self moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];}else {//有多组id fromData = _tempDataSource[fromIndexPath.section][fromIndexPath.row];id toData = _tempDataSource[toIndexPath.section][toIndexPath.row];NSMutableArray *fromArray = [_tempDataSource[fromIndexPath.section] mutableCopy];NSMutableArray *toArray = [_tempDataSource[toIndexPath.section] mutableCopy];[fromArray replaceObjectAtIndex:fromIndexPath.row withObject:toData];[toArray replaceObjectAtIndex:toIndexPath.row withObject:fromData];[_tempDataSource replaceObjectAtIndex:fromIndexPath.section withObject:fromArray];[_tempDataSource replaceObjectAtIndex:toIndexPath.section withObject:toArray];//交换cell[self beginUpdates];[self moveRowAtIndexPath:fromIndexPath toIndexPath:toIndexPath];[self moveRowAtIndexPath:toIndexPath toIndexPath:fromIndexPath];[self endUpdates];}//交换数据源后reloadData[self reloadData];//返回交换后的数据源if (self.dataSource && [self.dataSource respondsToSelector:@selector(tableView:newDataSourceArrayAfterMove:)]) {[self.dataSource tableView:self newDataSourceArrayAfterMove:_tempDataSource.copy];}//此处用两个cell的截图实现交换的动画UITableViewCell *cell = [self cellForRowAtIndexPath:fromIndexPath];cell.hidden = NO;UITableViewCell *cell1 = [self cellForRowAtIndexPath:toIndexPath];cell1.hidden = YES;UIView *tmpCell = [self dh_snapshotViewWithInputView:cell];cell.hidden = YES;tmpCell.frame = cell1.frame;if (_toBottom == -1) {//向上tmpCell.frame = CGRectMake(0, CGRectGetMinY(cell1.frame), cell.frame.size.width, cell.frame.size.height);[self insertSubview:tmpCell belowSubview:_tempView];}else if (_toBottom == 1) {//向下tmpCell.frame = CGRectMake(0, CGRectGetMaxY(cell1.frame)-cell.frame.size.height, cell.frame.size.width, cell.frame.size.height);[self insertSubview:tmpCell belowSubview:_tempView];}else{}[UIView animateWithDuration:0.2 animations:^{tmpCell.frame = cell.frame;}completion:^(BOOL finished) {cell.hidden = NO;[tmpCell removeFromSuperview];}];
}
复制代码

6.边缘滚动处理

- (void)dh_startEdgeScroll{    _edgeScrollTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(dh_processEdgeScroll)];    [_edgeScrollTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];}

- (void)dh_processEdgeScroll{    [self dh_gestureChanged:_gesture];    CGFloat minOffsetY = self.contentOffset.y + _edgeScrollRange;    CGFloat maxOffsetY = self.contentOffset.y + self.bounds.size.height - _edgeScrollRange;    CGPoint touchPoint = _tempView.center;    //处理上下达到极限之后不再滚动tableView,其中处理了滚动到最边缘的时候,当前处于edgeScrollRange内,但是tableView还未显示完,需要显示完tableView才停止滚动    if (touchPoint.y < _edgeScrollRange) {        if (self.contentOffset.y <= 0) {            return;        }else {            if (self.contentOffset.y - 1 < 0) {                return;            }            [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y - 1) animated:NO];            _tempView.center = CGPointMake(_tempView.center.x, _tempView.center.y - 1);        }    }    if (touchPoint.y > self.contentSize.height - _edgeScrollRange) {        if (self.contentOffset.y >= self.contentSize.height - self.bounds.size.height) {            return;        }else {            if (self.contentOffset.y + 1 > self.contentSize.height - self.bounds.size.height) {                return;            }            [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + 1) animated:NO];            _tempView.center = CGPointMake(_tempView.center.x, _tempView.center.y + 1);        }    }    //处理滚动    CGFloat maxMoveDistance = 20;    if (touchPoint.y < minOffsetY) {        //cell在往上移动        CGFloat moveDistance = (minOffsetY - touchPoint.y)/_edgeScrollRange*maxMoveDistance;        [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y - moveDistance) animated:NO];        _tempView.center = CGPointMake(_tempView.center.x, _tempView.center.y - moveDistance);    }else if (touchPoint.y > maxOffsetY) {        //cell在往下移动        CGFloat moveDistance = (touchPoint.y - maxOffsetY)/_edgeScrollRange*maxMoveDistance;        [self setContentOffset:CGPointMake(self.contentOffset.x, self.contentOffset.y + moveDistance) animated:NO];        _tempView.center = CGPointMake(_tempView.center.x, _tempView.center.y + moveDistance);    }}复制代码

转载于:https://juejin.im/post/5a37791f6fb9a044fa19f5a1

UITableView长按拖动排序(支持不同行高,不同section间交换)相关推荐

  1. iOS tableview左滑编辑,长按拖动排序

    有的时候还是想复用iOS自身的设计逻辑,减少代码编写. 本次主要是想实现对于列表的排序,编辑修改,删除操作. 涉及到的操作方式如下: 向左滑动出现编辑及删除选项: 长按列表项目排序: 效果图如下: 先 ...

  2. 【Android 事件分发】ItemTouchHelper 实现拖动排序

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  3. Android拖动实现(一个流畅的拖动排序DragSortGridView,自动滚屏)

    https://github.com/huxq17/HandyGridView 先上效果 流畅效果超越了网易新闻和UC浏览器的栏目收藏.gif图和实际效果有差距 1.拖拽可以移动item,并且其他it ...

  4. 移动端上下拖动调整顺序效果_vue实现移动端拖动排序

    本文实例为大家分享了vue实现移动端拖动排序的具体代码,供大家参考,具体内容如下 效果 代码: class="childDiv" v-for="(option,index ...

  5. Android实现GridView的item长按拖动删除完美实现(带动画效果)

    领导这几天让做一个项目,就是可以实现像支付宝首页一样的可以长按拖动,删除的界面,以前没做过,领导让我做的时候觉得简直是老虎吃天,无从下手啊,可是领导的任务还是要实现的,没办法,就自己网上找咯,但是网上 ...

  6. php 长连接心跳_支持gRPC长链接,深度解读Nacos2.0架构设计及新模型

    作者 | 杨翊(席翁) Nacos PMC 来源|阿里巴巴云原生公众号 Nacos 简介 Nacos 在阿里巴巴起源于 2008 年五彩石项目,该项目完成了微服务拆分和业务中台建设,随着云计算和开源环 ...

  7. android 二级列表拖动排序_Excel的数据透视表六种排序方法

    Excel的数据透视表排序不像表格中那样操作灵活,很多小伙伴对此不熟悉,本文系统讲解数据透视表的各种排序. 一.常规排序 二.组内排序 三.多关键字排序 四.手动拖动排序 五.手动输入排序 六.设置透 ...

  8. jquery 鼠标拖动排序Li或Table

    1.前端页面 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="拖动排序Li或Ta ...

  9. 西门子1200/1500PLC不定长数组选择排序的运用编程实例

    前景介绍: 1.选择排序原理:选择排序算法首先从第1个位置开始对全部元素进行选择,选出全部元素中最小的给该位置,再对第2个位置进行选择,在剩余元素中选择最小的给该位置即可:以此类推,重复进行" ...

最新文章

  1. Nature:要想真正研究宿主-肠道微生物的相互作用,必须将相对定量变成绝对定量...
  2. Redis的三种启动方式【转】
  3. 201117阶段二SQLite数据库
  4. Action和Func区别
  5. A*算法(一)算法导言
  6. Android学习之路五:Dialog和Toast
  7. sonar小白式入门
  8. matlab示例程序,matlab示例程序
  9. 数据库学生信息管理系统
  10. 计划bom表 java_ERP总结系列(BOM浅谈)
  11. latex中自动生成参考文献
  12. ImportError: cannot import name ‘evaluate‘ from ‘surprise‘解决方案
  13. 【ArcGIS】空间表无法删除的问题处理
  14. 入门理解计算机视觉、图形学、图像处理
  15. Kali实现ARP欺骗
  16. Flowable 6.6.0 BPMN用户指南 - (5)Spring Boot - 5.8 Flowable应用属性
  17. python绘制日历图
  18. 第二十章:异步和文件I/O.(十九)
  19. [mini-css-extract-plugin] warning Conflicting order
  20. 越南使用的越南文unicode编码范围

热门文章

  1. string转int/float/double、int/float/double转string、转字符串数组的方法:stoi、stringstream、scanf、to_string、sprintf
  2. 【离散数学】滨江学院 期末考试 题库
  3. Oracle和al,ORACLEAL TERTABLE
  4. Swift NSDate的一个分类,把Mon Apr 04 19:45:37 +0800 2016这种格式的时间转换为2016-04-04 11:45:37 +0000
  5. 读《程序员修炼之道——从小工到专家》
  6. kuangbin专题一 简单搜索
  7. Android 驱动测试程序H-M-S 6
  8. js open 和close 方法
  9. C# 禁止控件重绘(绘制)
  10. 上学的时候的一个作业