iOS使用UICollectionView实现拖拽移动单元格

2022-10-07,,,

本文实例为大家分享了ios开发uicollectionview拖拽移动单元格的具体代码,供大家参考,具体内容如下

一.介绍

ios9提供api实现单元格排序呢功能,使用uicollectionview及其代理方法.ios9之后有自带方法可以实现该效果,只需添加长按手势,实现手势方法和调用ios9的api交换数据,ios9之前需要自己写方法实现这效果,除了要添加长按手势,这里还需要利用截图替换原理,手动计算移动位置来处理视图交换和数据交换.

二.方法和步骤

1.创建工程项目和视图控制器,如下图

2.声明对象和设置代理和数据源代理

@interface viewcontroller ()<uicollectionviewdelegate,uicollectionviewdatasource,uicollectionviewdelegateflowlayout>
 
@property (nonatomic, strong) nsmutablearray *dataarr;
@property (nonatomic, strong) uicollectionview *collectionview;
/**之前选中cell的nsindexpath*/
@property (nonatomic, strong) nsindexpath *oldindexpath;
/**单元格的截图*/
@property (nonatomic, strong) uiview *snapshotview;
/**之前选中cell的nsindexpath*/
@property (nonatomic, strong) nsindexpath *moveindexpath;
 
@end

3.初始化uicollectionview,并添加长按手势,在viewdidload中初始化

cgfloat screen_width = self.view.frame.size.width;
    uicollectionviewflowlayout *flowlayout = [[uicollectionviewflowlayout alloc] init];
    flowlayout.itemsize = cgsizemake((screen_width-40.0)/3, (screen_width-40.0)/3);
    uicollectionview *collectionview = [[uicollectionview alloc] initwithframe:cgrectmake(0, 50.0, screen_width, (screen_width-40.0)/3+20.0) collectionviewlayout:flowlayout];
    collectionview.datasource = self;
    collectionview.delegate = self;
    collectionview.backgroundcolor = [uicolor whitecolor];
    [collectionview registerclass:[uicollectionviewcell class] forcellwithreuseidentifier:@"uicollectionviewcell"];
    [self.view addsubview:self.collectionview = collectionview];
    
    // 添加长按手势
    uilongpressgesturerecognizer *longpress = [[uilongpressgesturerecognizer alloc] initwithtarget:self action:@selector(handlelonggesture:)];
    [collectionview addgesturerecognizer:longpress];

4.实例化数据源,(50个随机颜色,透明度0.8),在viewdidload中初始化

self.dataarr = [[nsmutablearray alloc] init];
for (nsinteger index = 0; index < 50; index ++) {
        cgfloat hue = (arc4random()%256/256.0); //0.0 到 1.0
        cgfloat saturation = (arc4random()%128/256.0)+0.5; //0.5 到 1.0
        cgfloat brightness = (arc4random()%128/256.0)+0.5; //0.5 到 1.0
        uicolor *color = [uicolor colorwithhue:hue saturation:saturation brightness:brightness alpha:0.5];
        [self.dataarr addobject:color];
    }

5.实现uicollectionview的uicollectionviewdatasource的两个必须实现的方法

#pragma mark - uicollectionviewdatasource
- (nsinteger)collectionview:(uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section
{
    return self.dataarr.count;
}
 
- (uicollectionviewcell *)collectionview:(uicollectionview *)collectionview cellforitematindexpath:(nsindexpath *)indexpath
{
    uicollectionviewcell *cell = [collectionview dequeuereusablecellwithreuseidentifier:@"uicollectionviewcell" forindexpath:indexpath];
    cell.backgroundcolor = self.dataarr[indexpath.row];
    return cell;
}

6.重点来了,实现长按手势方法

#pragma mark - 长按手势
- (void)handlelonggesture:(uilongpressgesturerecognizer *)longpress
{
    if ([[[uidevice currentdevice] systemversion] floatvalue] < 9.0) {
        [self action:longpress];
    } else {
        [self ios9_action:longpress];
    }
}

7.ios9之后的实现

#pragma mark - ios9 之后的方法
- (bool)collectionview:(uicollectionview *)collectionview canmoveitematindexpath:(nsindexpath *)indexpath
{
    // 返回yes允许row移动
    return yes;
}
 
- (void)collectionview:(uicollectionview *)collectionview moveitematindexpath:(nsindexpath *)sourceindexpath toindexpath:(nsindexpath *)destinationindexpath
{
    //取出移动row数据
    id color = self.dataarr[sourceindexpath.row];
    //从数据源中移除该数据
    [self.dataarr removeobject:color];
    //将数据插入到数据源中的目标位置
    [self.dataarr insertobject:color atindex:destinationindexpath.row];
}
 
- (void)ios9_action:(uilongpressgesturerecognizer *)longpress
{
    switch (longpress.state) {
        case uigesturerecognizerstatebegan:
        { //手势开始
            //判断手势落点位置是否在row上
            nsindexpath *indexpath = [self.collectionview indexpathforitematpoint:[longpress locationinview:self.collectionview]];
            if (indexpath == nil) {
                break;
            }
            uicollectionviewcell *cell = [self.collectionview cellforitematindexpath:indexpath];
            [self.view bringsubviewtofront:cell];
            //ios9方法 移动cell
            [self.collectionview begininteractivemovementforitematindexpath:indexpath];
        }
            break;
        case uigesturerecognizerstatechanged:
        { // 手势改变
            // ios9方法 移动过程中随时更新cell位置
            [self.collectionview updateinteractivemovementtargetposition:[longpress locationinview:self.collectionview]];
        }
            break;
        case uigesturerecognizerstateended:
        { // 手势结束
            // ios9方法 移动结束后关闭cell移动
            [self.collectionview endinteractivemovement];
        }
            break;
        default: //手势其他状态
            [self.collectionview cancelinteractivemovement];
            break;
    }
}

8.ios9之前的实现

#pragma mark - ios9 之前的方法
- (void)action:(uilongpressgesturerecognizer *)longpress
{
    switch (longpress.state) {
        case uigesturerecognizerstatebegan:
        { // 手势开始
            //判断手势落点位置是否在row上
            nsindexpath *indexpath = [self.collectionview indexpathforitematpoint:[longpress locationinview:self.collectionview]];
            self.oldindexpath = indexpath;
            if (indexpath == nil) {
                break;
            }
            uicollectionviewcell *cell = [self.collectionview cellforitematindexpath:indexpath];
            // 使用系统的截图功能,得到cell的截图视图
            uiview *snapshotview = [cell snapshotviewafterscreenupdates:no];
            snapshotview.frame = cell.frame;
            [self.view addsubview:self.snapshotview = snapshotview];
            // 截图后隐藏当前cell
            cell.hidden = yes;
            
            cgpoint currentpoint = [longpress locationinview:self.collectionview];
            [uiview animatewithduration:0.25 animations:^{
                snapshotview.transform = cgaffinetransformmakescale(1.05, 1.05);
                snapshotview.center = currentpoint;
            }];
        }
            break;
        case uigesturerecognizerstatechanged:
        { // 手势改变
            //当前手指位置 截图视图位置随着手指移动而移动
            cgpoint currentpoint = [longpress locationinview:self.collectionview];
            self.snapshotview.center = currentpoint;
            // 计算截图视图和哪个可见cell相交
            for (uicollectionviewcell *cell in self.collectionview.visiblecells) {
                // 当前隐藏的cell就不需要交换了,直接continue
                if ([self.collectionview indexpathforcell:cell] == self.oldindexpath) {
                    continue;
                }
                // 计算中心距
                cgfloat space = sqrtf(pow(self.snapshotview.center.x - cell.center.x, 2) + powf(self.snapshotview.center.y - cell.center.y, 2));
                // 如果相交一半就移动
                if (space <= self.snapshotview.bounds.size.width / 2) {
                    self.moveindexpath = [self.collectionview indexpathforcell:cell];
                    //移动 会调用willmovetoindexpath方法更新数据源
                    [self.collectionview moveitematindexpath:self.oldindexpath toindexpath:self.moveindexpath];
                    //设置移动后的起始indexpath
                    self.oldindexpath = self.moveindexpath;
                    break;
                }
            }
        }
            break;
        default:
        { // 手势结束和其他状态
            uicollectionviewcell *cell = [self.collectionview cellforitematindexpath:self.oldindexpath];
            // 结束动画过程中停止交互,防止出问题
            self.collectionview.userinteractionenabled = no;
            // 给截图视图一个动画移动到隐藏cell的新位置
            [uiview animatewithduration:0.25 animations:^{
                self.snapshotview.center = cell.center;
                self.snapshotview.transform = cgaffinetransformmakescale(1.0, 1.0);
            } completion:^(bool finished) {
                // 移除截图视图,显示隐藏的cell并开始交互
                [self.snapshotview removefromsuperview];
                cell.hidden = no;
                self.collectionview.userinteractionenabled = yes;
            }];
        }
            break;
    }
}

三.ios9之后添加的api

// support for reordering
- (bool)begininteractivemovementforitematindexpath:(nsindexpath *)indexpath ns_available_ios(9_0); // returns no if reordering was prevented from beginning - otherwise yes
- (void)updateinteractivemovementtargetposition:(cgpoint)targetposition ns_available_ios(9_0);
- (void)endinteractivemovement ns_available_ios(9_0);
- (void)cancelinteractivemovement ns_available_ios(9_0);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

《iOS使用UICollectionView实现拖拽移动单元格.doc》

下载本文的Word格式文档,以方便收藏与打印。