欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

iOS开发UICollectionView实现拖拽效果

程序员文章站 2023-11-22 11:10:04
一.介绍 ios9提供api实现单元格排序功能,使用uicollectionview及其代理方法。ios9之后有自带方法可以实现该效果,只需添加长按手势,实现手势方法和调...

一.介绍

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

二.方法和步骤

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

iOS开发UICollectionView实现拖拽效果

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);


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