美文网首页寒哥管理的技术专题iOS小项目tom
iOS_UI进阶【拖拽排序】的实现

iOS_UI进阶【拖拽排序】的实现

作者: HelloYeah | 来源:发表于2016-11-21 13:59 被阅读3564次

    导读

    拖拽排序是新闻类的App可以说是必有的交互设计,如今日头条,网易新闻等。拖拽排序是一个交互体验非常好的设计,简单,方便。

    今日头条的拖拽排序界面
    今日头条的拖拽排序界面.png
    我实现的长按拖拽排序效果
    长按拖拽排序.gif
    实现方案

    1.给CollectionViewCell添加一个长按手势,通过代理把手势传递到collectionView所在的控制器中。
    - (void)awakeFromNib{
    self.layer.cornerRadius = 3;
    self.layer.masksToBounds = YES;
    //给每个cell添加一个长按手势
    UILongPressGestureRecognizer * longPress =[[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];
    longPress.delegate = self;
    [self addGestureRecognizer:longPress];
    }

    - (void)longPress:(UILongPressGestureRecognizer *)longPress{
        if (self.delegate && [self.delegate respondsToSelector:@selector(longPress:)]) {
            [self.delegate longPress:longPress];
        }
    }
    

    2.开始长按时对cell进行截图,并隐藏cell。

    - (void)longPress:(UILongPressGestureRecognizer *)longPress{
        //记录上一次手势的位置
        static CGPoint startPoint;
        //触发长按手势的cell
        MovingCell * cell = (MovingCell *)longPress.view;
        //开始长按
        if (longPress.state == UIGestureRecognizerStateBegan) {
            [self shakeAllCell];
            //获取cell的截图
            _snapshotView  = [cell snapshotViewAfterScreenUpdates:YES];
            _snapshotView.center = cell.center;
            [_collectionView addSubview:_snapshotView];
            _indexPath= [_collectionView indexPathForCell:cell];
            _originalCell = cell;
            _originalCell.hidden = YES;
            startPoint = [longPress locationInView:_collectionView];
        }
    

    3、在手势移动的时候,移动截图视图,用遍历的方法求出截图移动到哪个cell的位置,再调用系统的api交换这个cell和隐藏cell的位置,并且数据源中的数据也需要调整顺序

        //手势移动的时候
        else if (longPress.state == UIGestureRecognizerStateChanged){
        CGFloat tranX = [longPress locationOfTouch:0 inView:_collectionView].x - startPoint.x;
        CGFloat tranY = [longPress locationOfTouch:0 inView:_collectionView].y - startPoint.y;
        //设置截图视图位置
        _snapshotView.center = CGPointApplyAffineTransform(_snapshotView.center, CGAffineTransformMakeTranslation(tranX, tranY));
        startPoint = [longPress locationOfTouch:0 inView:_collectionView];
        //计算截图视图和哪个cell相交
        for (UICollectionViewCell *cell in [_collectionView visibleCells]) {
            //跳过隐藏的cell
            if ([_collectionView indexPathForCell:cell] == _indexPath) {
                continue;
            }
            //计算中心距
            CGFloat space = sqrtf(pow(_snapshotView.center.x - cell.center.x, 2) + powf(_snapshotView.center.y - cell.center.y, 2));
    
            //如果相交一半且两个视图Y的绝对值小于高度的一半就移动
            if (space <= _snapshotView.bounds.size.width * 0.5 && (fabs(_snapshotView.center.y - cell.center.y) <= _snapshotView.bounds.size.height * 0.5)) {
                _nextIndexPath = [_collectionView indexPathForCell:cell];
                if (_nextIndexPath.item > _indexPath.item) {
                    for (NSUInteger i = _indexPath.item; i < _nextIndexPath.item ; i ++) {
                        [self.array exchangeObjectAtIndex:i withObjectAtIndex:i + 1];
                    }
                }else{
                    for (NSUInteger i = _indexPath.item; i > _nextIndexPath.item ; i --) {
                        [self.array exchangeObjectAtIndex:i withObjectAtIndex:i - 1];
                    }
                }
                //移动
                [_collectionView moveItemAtIndexPath:_indexPath toIndexPath:_nextIndexPath];
                //设置移动后的起始indexPath
                _indexPath = _nextIndexPath;
                break;
            }
        }
    

    4.手势停止时,移除截图的view,显示隐藏cell

    //手势停止时
    }else if(longPress.state == UIGestureRecognizerStateEnded){
        [self stopShake];
        [_snapshotView removeFromSuperview];
        _originalCell.hidden = NO;
    }
    
    其他

    代码还可以进一步封装,写一个数据管理类dataTool,dataTool作为collectionView的数据源,所有的数据源方法都写到dataTool类中。手势的代理方法也在里面实现,这样控制器会简洁很多,控制器就不需要关注拖拽排序的具体逻辑了。大家有空可以自己写写看,也许你们有更好的处理方案,可以评论交流一下。
    github地址:https://github.com/HelloYeah/DraggingSort

    提示

    如果你们是从iOS9开始适配的话,那么可以用系统的Api,非常简单好用

    //是否允许拖拽
     - (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(9_0);
     /**
      *   从sourceIndexPath 拖拽至destinationIndexPath,在这个代理方法里修改数据源即可。
      *   exchangeObjectAtIndex:sourceIndexPath.item withObjectAtIndex: destinationIndexPath.item
      */
     - (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath*)destinationIndexPath NS_AVAILABLE_IOS(9_0);

    相关文章

      网友评论

      • MissLu16:有tableview拖拽排序吗
      • 7d7f6134be8b:好有用😊
      • 被帅醒的小吴同志:用collection view楼主不会有重用问题么?
        HelloYeah:嗯。会有这个问题。
        被帅醒的小吴同志:@HelloYeah 如果你做长按放大功能当滑出屏幕外也许就会出现重用
        HelloYeah:没问题
      • TenMios:学习了
      • 难却却:先Mark一下
      • 1ccf27ae0981:https://github.com/qing-song/DragGridDemo 这个是看到博主的文章后,参考 GSD_iOS 模仿某宝时写的拖拽效果,然后摘出来了,跟你的做了一下对比
        HelloYeah:@qs_hero 嗯。我的功能还没完善
      • 一枚霉:我下的demo为什么显示Tool目录下YLDragSortTool 的两个文件不存在啊?
        一枚霉:@HelloYeah OK :+1:
        HelloYeah:@一枚霉 你再试试
        HelloYeah:@一枚霉 额,我看看
      • af4e8a35bc89:很喜欢。不过在我这里,截屏cell为什么是一个空白视图
        af4e8a35bc89:@HelloYeah 其实是因为用模拟器调试出现空白视图,使用真机没问题。但是,当数量太多,往下拖,不会自动下滑,而且经常出现白屏导致不能使用。控件的思路和实现都很好,剩下的就是解决这些问题,优化成一个可以真正直接用在项目中的控件,才到位。期待完美版~!
        HelloYeah:@江湖丽日 建议打开UI层次结构图看看,比较容易查找原因。
      • 夏天的风_song:喜欢,思路很清晰 :smile:
      • 春泥Fu:66666
      • 0505bad4b897:支持开源,不错。不过demo还是有些问题,比如如果数据好多,拖动到底部时不会自动向上滑
        af4e8a35bc89:@其实你懂的 不会啊 试了下 滑到底部就停了 偶尔还会出现多乱
        其实你懂De:@剑尖 会 数据多了 拖动到底部时会自动向上滑
        HelloYeah:这个功能的确没做

      本文标题:iOS_UI进阶【拖拽排序】的实现

      本文链接:https://www.haomeiwen.com/subject/hrcrpttx.html