iOS开发UICollectionView实现拖拽效果

时间:2022-11-01 17:39:12

一.介绍

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

二.方法和步骤

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

iOS开发UICollectionView实现拖拽效果

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

?
1
2
3
4
5
6
7
8
9
10
11
12
@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中初始化

?
1
2
3
4
5
6
7
8
9
10
11
12
13
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中初始化

?
1
2
3
4
5
6
7
8
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的两个必须实现的方法

?
1
2
3
4
5
6
7
8
9
10
11
12
#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.重点来了,实现长按手势方法

?
1
2
3
4
5
6
7
8
9
#pragma mark - 长按手势
- (void)handlelonggesture:(uilongpressgesturerecognizer *)longpress
{
  if ([[[uidevice currentdevice] systemversion] floatvalue] < 9.0) {
    [self action:longpress];
  } else {
    [self ios9_action:longpress];
  }
}

7.ios9之后的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#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之前的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#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如下

?
1
2
3
4
5
// 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);

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

原文链接:https://blog.csdn.net/wgl_happy/article/details/52179608