废话不多说直接上代码了,其核心实现代码就是在-(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
效果图:(做的有点难看,大家将就~~)

进阶版效果图