使用CollectionView实现Item不同Section间的移动、拖拽

废话不多说直接上代码了,其核心实现代码就是在-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath方法中的代码

#import "MyChannelView.h"//本类
#import "MyChannelCollectionViewCell.h"// 自定义cell

#define  MyChannelCollectionViewCellID  @"MyChannelCollectionViewCellID"

@interface MyChannelView ()<UICollectionViewDelegate,UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>

@property (nonatomic,strong) UICollectionView *myChannelCollectionView;
@property (nonatomic,strong) NSMutableArray *selectedArray;// 0区数据源
@property (nonatomic,strong) NSMutableArray *deselectedArray;// 1区数据源
@property (nonatomic,strong) NSArray *allArray;//总数据源

@end

@implementation MyChannelView

-(instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.selectedArray = [NSMutableArray arrayWithArray:@[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10"]];
        self.deselectedArray = [NSMutableArray arrayWithArray:@[@"一", @"二", @"三", @"四", @"五", @"六", @"七", @"八", @"九", @"十"]];
        self.allArray = @[self.selectedArray, self.deselectedArray];
        
        UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
        layout.minimumLineSpacing = 5;
        layout.minimumInteritemSpacing = 5;
        layout.scrollDirection = UICollectionViewScrollDirectionVertical;

        self.myChannelCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 44, frame.size.width, frame.size.height-44) collectionViewLayout:layout];
        self.myChannelCollectionView.delegate = self;
        self.myChannelCollectionView.dataSource = self;
        self.myChannelCollectionView.backgroundColor = [UIColor whiteColor];
        [self addSubview:self.myChannelCollectionView];
        
        [self.myChannelCollectionView registerClass:[MyChannelCollectionViewCell class] forCellWithReuseIdentifier:MyChannelCollectionViewCellID];
    }
    return self;
}
// delegate/dateSource method
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return self.allArray.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    switch (section) {
        case 0:
            return self.selectedArray.count;
            break;
            
        default:
            return self.deselectedArray.count;
            break;
    }
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyChannelCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:MyChannelCollectionViewCellID forIndexPath:indexPath];
    [cell.titleButton setTitle:self.allArray[indexPath.section][indexPath.row] forState:(UIControlStateNormal)];// 我这里自定义cell中就一个button
    return cell;
}
// 分组间上左下右间距
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIEdgeInsetsMake(10, 10, 10, 10);
}
// cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(ceilf((kGScreenWidth-50)/4), 44);
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
   // 这里是重点!
    if (indexPath.section == 0) {
        NSString * one = self.selectedArray[indexPath.row];
        [self.selectedArray removeObject:one];
        [self.deselectedArray insertObject:one atIndex:0];
        self.allArray = @[self.selectedArray, self.deselectedArray];
// 在这里是当你点选某个cell后希望他去哪一区,则将其移除于本数据源同时加入到你希望他去的那个数据源即可。然后调用下面的方法
        [collectionView moveItemAtIndexPath:indexPath toIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]];
// 在调用该方法前一定要先更改数据源!
    }else{
        NSString * one = self.deselectedArray[indexPath.row];
        [self.deselectedArray removeObject:one];
        [self.selectedArray addObject:one];
        self.allArray = @[self.selectedArray, self.deselectedArray];
        [collectionView moveItemAtIndexPath:indexPath toIndexPath:[NSIndexPath indexPathForItem:self.selectedArray.count-1 inSection:0]];
    }
}

@end

让我们看一下效果:

效果图
以下是进阶版:
#import "MyChannelView.h" // 本类
#import "MyChannelCollectionViewCell.h" // 自定义cell
#import "ChannelModel.h"  //数据model类 里面只有两个属性:NSString *ID 、 NSString *name;
#import "MyChannelHeaderView.h" // 自定义section头视图 该视图包括三个控件,UILabel *titleLabel(标题)、UILabel *detaileLabel(说明)、UIButton *editButton(编辑按钮);
// 重用标识
#define  MyChannelCollectionViewCellID  @"MyChannelCollectionViewCellID"
#define HeaderViewID @"SectionHeaderID"

@interface MyChannelView ()<UICollectionViewDelegate,UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
{
    BOOL isEdit; //记录我的频道是否处于编辑状态
    BOOL isChanged; //记录cell 是否移动
}
@property (nonatomic,strong) UIButton *missButton; // 该button上放了一张“X”号图片 为取消按钮
@property (nonatomic,strong) UICollectionView *myChannelCollectionView;
@property (nonatomic,strong) NSArray *allArray;
@property (nonatomic,strong) NSMutableArray *selectedArray;
@property (nonatomic,strong) NSMutableArray *deselectedArray;
@property (nonatomic,strong) UIPanGestureRecognizer * panGestureRecognizer; // cell上的拖拽手势
@property (nonatomic,strong) NSMutableArray *cellAttributesArray;//记录所有cell的attributes
@property (nonatomic,strong) UICollectionViewLayoutAttributes *attributes_isChange; // 记录cell拖拽手势结束后应该在的位置attributes(仅在section为0时cell间拖拽移动使用)
@end

@implementation MyChannelView
-(instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor whiteColor];
        self.cellAttributesArray = [NSMutableArray array];
        self.missButton = [UIButton new];
        [self.missButton setImage:[UIImage imageNamed:@"missImage"] forState:(UIControlStateNormal)];
        [self.missButton addTarget:self action:@selector(missButtonAction) forControlEvents:(UIControlEventTouchUpInside)];
        [self addSubview:self.missButton];
        isEdit = NO; //初始是不处于编辑状态
        UICollectionViewFlowLayout * layout = [[UICollectionViewFlowLayout alloc] init];
        layout.minimumLineSpacing = 5;
        layout.minimumInteritemSpacing = 5;
        layout.scrollDirection = UICollectionViewScrollDirectionVertical;
        self.myChannelCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 64, frame.size.width, frame.size.height-64) collectionViewLayout:layout];
        self.myChannelCollectionView.delegate = self;
        self.myChannelCollectionView.dataSource = self;
        self.myChannelCollectionView.backgroundColor = [UIColor whiteColor];
        [self addSubview:self.myChannelCollectionView];
        
        [self.myChannelCollectionView registerClass:[MyChannelCollectionViewCell class] forCellWithReuseIdentifier:MyChannelCollectionViewCellID];
        [self.myChannelCollectionView registerClass:[MyChannelHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:HeaderViewID];
        
        [self requestForData]; // 加载数据
    }
    return self;
}
- (void)layoutSubviews
{
    [self.missButton makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(@20);
        make.right.equalTo(@-10);
        make.width.equalTo(@44);
        make.height.equalTo(@44);
    }];
    self.myChannelCollectionView.frame = CGRectMake(0, 64, self.frame.size.width, self.frame.size.height-64);// 这里是做了转屏功能所以····
}
- (void)missButtonAction
{
    [self removeFromSuperview];
}
// 我的频道界面数据
- (void)requestForData
{// 这里是做的数据请求,就省略了,见谅。结果类似如下:
      self.selectedArray = [NSMutableArray arrayWithArray:@[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10"]];
      self.deselectedArray = [NSMutableArray arrayWithArray:@[@"一", @"二", @"三", @"四", @"五", @"六", @"七", @"八", @"九", @"十"]];
      self.allArray = @[self.selectedArray, self.deselectedArray];
      [self.myChannelCollectionView reloadData];
// 不过演示中数组元素都是model
}
// delegate/dateSource method
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    return self.allArray.count;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    switch (section) {
        case 0:
            return self.selectedArray.count;
            break;
        default:
            return self.deselectedArray.count;
            break;
    }
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    MyChannelCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:MyChannelCollectionViewCellID forIndexPath:indexPath];
    self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognizerAction:)];
    ChannelModel * model = self.allArray[indexPath.section][indexPath.row];
    [cell.titleButton setTitle:model.name forState:(UIControlStateNormal)];
    if (isEdit && indexPath.section == 0) {
        cell.editedButton.hidden = NO;
        [cell addGestureRecognizer:self.panGestureRecognizer];
    }else{// 隐藏表示处于编辑状态的按钮并移除拖拽手势
        cell.editedButton.hidden = YES;
        NSArray * gestureArray = [cell gestureRecognizers];
        for (UIGestureRecognizer * gesture in gestureArray) {
            if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
                [cell removeGestureRecognizer:gesture];
            }
        }
    }
    return cell;
}
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
    UICollectionReusableView * headerView = nil;
    if (kind == UICollectionElementKindSectionHeader) {
        MyChannelHeaderView * channelHeaderView = (MyChannelHeaderView *)[collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:HeaderViewID forIndexPath:indexPath];
        if (indexPath.section == 0) {
            channelHeaderView.titleLabel.text = @"我的频道";
            if (isEdit) {
                channelHeaderView.detaileLabel.hidden = NO;
            }else{
                channelHeaderView.detaileLabel.hidden = YES;
            }
            channelHeaderView.editButton.hidden = NO;
            [channelHeaderView.editButton addTarget:self action:@selector(channelHeaderViewAction) forControlEvents:(UIControlEventTouchUpInside)]; 
            MyindexPath = indexPath;
        }else{
            channelHeaderView.titleLabel.text = @"点击添加频道";
            channelHeaderView.editButton.hidden = YES;
            channelHeaderView.detaileLabel.hidden = YES;
        }
        headerView = (UICollectionReusableView *)channelHeaderView;
    }
    return headerView;
}
#pragma  编辑按钮点击事件
- (void)channelHeaderViewAction
{
    isEdit = !isEdit;
    [self.myChannelCollectionView reloadData];
}
// 分组间上左下右间距
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIEdgeInsetsMake(10, 10, 10, 10);
}
// header size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout referenceSizeForHeaderInSection:(NSInteger)section
{
    return CGSizeMake(kGScreenWidth, 44);
}
// cell size
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(ceilf((kGScreenWidth-50)/4), 44);
}
//cell的点击事件
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    
    MyChannelCollectionViewCell * cell = (MyChannelCollectionViewCell *)[collectionView cellForItemAtIndexPath:indexPath];
    if (indexPath.section == 0) {
#pragma 当处于编辑状态时点击会使零区cell移动到一区,不处于编辑状态则会触发其他事件
        if (isEdit) {
            ChannelModel * one = self.selectedArray[indexPath.row];
            [self.selectedArray removeObject:one];
            [self.deselectedArray insertObject:one atIndex:0];
            self.allArray = @[self.selectedArray, self.deselectedArray];
            [self setAllArrayToDefault:self.allArray];
            NSArray * gestureArray = [cell gestureRecognizers];
            for (UIGestureRecognizer * gesture in gestureArray) {
                if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
                    [cell removeGestureRecognizer:gesture];
                }
            }
            [collectionView moveItemAtIndexPath:indexPath toIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]];
            cell.editedButton.hidden = YES;
        }else{ // 触发其他事件,e.g  :跳转到其他页面等
        }
    }else{
#pragma 一区的cell只能点击 不能拖拽,点击后只会移动到零区同时添加拖拽手势
        ChannelModel * one = self.deselectedArray[indexPath.row];
        [self.deselectedArray removeObject:one];
        [self.selectedArray addObject:one];
        self.allArray = @[self.selectedArray, self.deselectedArray];
        [self setAllArrayToDefault:self.allArray];
        [cell addGestureRecognizer:self.panGestureRecognizer];
        [collectionView moveItemAtIndexPath:indexPath toIndexPath:[NSIndexPath indexPathForItem:self.selectedArray.count-1 inSection:0]];
        if (isEdit) {
            cell.editedButton.hidden = NO;
        }else{
            cell.editedButton.hidden = YES;
        }
    }
}
#pragma  拖拽手势响应事件
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)sender
{
    MyChannelCollectionViewCell *cell = (MyChannelCollectionViewCell *)sender.view;
    NSIndexPath *cellIndexPath = [self.myChannelCollectionView indexPathForCell:cell];
    //将拖拽的cell提于视图最前面
    [self.myChannelCollectionView bringSubviewToFront:cell];
    //  手势响应的三个状态
    if (sender.state == UIGestureRecognizerStateBegan) {
        [self.cellAttributesArray removeAllObjects];
        for (int i = 0;i< self.allArray.count; i++) {
            NSMutableArray * tempArray = [NSMutableArray array];
            NSArray * countArray = self.allArray[i];
            for (int j = 0; j < countArray.count; j++) {
  #pragma 这里是获取每一个cell的属性集合(Attributes),并按区区分开。很重要!
                [tempArray addObject:[self.myChannelCollectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:j inSection:i]]];
            }
            [self.cellAttributesArray addObject:tempArray];
        }
    }else if (sender.state == UIGestureRecognizerStateChanged){
        cell.center = [sender locationInView:self.myChannelCollectionView]; // 让cell时刻跟随手势移动
        NSArray *tempArr = self.cellAttributesArray[0];
        for (UICollectionViewLayoutAttributes *attributes in tempArr) {
#pragma 判断Attributes集合中的元素的fram是否包含cell的center,同时不能使cell本身且分区必须为0 才能成功地移动换位置
            if (CGRectContainsPoint(attributes.frame, cell.center) && cellIndexPath != attributes.indexPath && attributes.indexPath.section == 0) {
                isChanged = YES; // 标识-cell已经和其他cell换了位置
                //对数组中存放的元素重新排序
                ChannelModel * one = self.selectedArray[cellIndexPath.row];
                [self.selectedArray removeObject:one];
                [self.selectedArray insertObject:one atIndex:attributes.indexPath.row];
                self.allArray = @[self.selectedArray, self.deselectedArray];
                [self setAllArrayToDefault:self.allArray];
                [self.myChannelCollectionView moveItemAtIndexPath:cellIndexPath toIndexPath:attributes.indexPath];
                self.attributes_isChange = attributes; // 移动完成后记录被替换的cell的属性(用来矫正cell位置)
            }
        }
    }else if (sender.state == UIGestureRecognizerStateEnded) {
        if (!isChanged) { // 没有换位置(这里有两种情况1、拖拽到了一区;2、还在自己原来的位置上)
            UICollectionViewLayoutAttributes *attributes_done;
            for (NSArray *tempArr in self.cellAttributesArray) {
                for (UICollectionViewLayoutAttributes *attributes in tempArr) {
                    if (CGRectContainsPoint(attributes.frame, [sender locationInView:self.myChannelCollectionView])) {
                        attributes_done = attributes;
                    }
                }
            }
            if (attributes_done.indexPath.section == 1) {
                ChannelModel * one = self.selectedArray[cellIndexPath.row];
                [self.selectedArray removeObject:one];
                [self.deselectedArray insertObject:one atIndex:0];
                self.allArray = @[self.selectedArray, self.deselectedArray];
                [self setAllArrayToDefault:self.allArray];
                NSArray * gestureArray = [cell gestureRecognizers];
                for (UIGestureRecognizer * gesture in gestureArray) {
                    if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
                        [cell removeGestureRecognizer:gesture];
                    }
                }
                cell.editedButton.hidden = YES;
                [self.myChannelCollectionView moveItemAtIndexPath:cellIndexPath toIndexPath:[NSIndexPath indexPathForItem:0 inSection:1]];
            }else{ // 位置未改变情况下位置矫正
                cell.center = [self.myChannelCollectionView layoutAttributesForItemAtIndexPath:cellIndexPath].center;
            }
        }else{// 已经移动后矫正位置
            cell.center = self.attributes_isChange.center;
        }
        isChanged = NO;
    }
}
@end

效果图:(做的有点难看,大家将就~~)

进阶版效果图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容