IOS 类似网易的频道选择功能、长按移动item、UICollectionView的高级使用

IOS的一些新闻客户端都有“频道选择”功能,用户可以自定义自己喜欢的频道查看内容。本文讲解如何实现频道的定制选择,实现了点击频道和长按拖拽频道的功能。
这里写图片描述
实现的思路和注意点如下:
1.通过实现关键方法和系统关键协议方法,实现点击item移动item的位置,不要忘记操作数据源!

[self.collectionView moveItemAtIndexPath:indexPath toIndexPath:EndIndexPath];//调用移动的方法。
- (void)collectionView:(UICollectionView )collectionView moveItemAtIndexPath:(NSIndexPath )sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath ;//点击移动的协议方法都要实现。

2.给UICollectionView添加长按手势,实现手势方法。实现长按item,item跟随手指移动,重排UICollectionView.关键方法如下:

//开始移动,根据indexPath路径
[self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];

//刷新位置
[self.collectionView updateInteractiveMovementTargetPosition:[gesture locationInView:self.collectionView]];

//手势结束时调用
[self.collectionView endInteractiveMovement];

//手势取消时调用
[self.collectionView cancelInteractiveMovement];

好了,上代码,关键地方已经注释,使用的自定义item和头部视图,希望可以帮助到你。

#import "ChannelChooseViewController.h"
#import "ChannelCollectionViewCell.h"
#import "ZWHeaderCollectionReusableView.h"

#define SCREEN_W [UIScreen mainScreen].bounds.size.width
#define COLOR_RGB(_R, _G, _B) [UIColor colorWithRed:_R/255.0f green:_G/255.0f blue:_B/255.0f alpha:1]

static NSString *const kCollectionViewCellIdentifier         = @"collectionViewCellIdentifier";             /**< 单元格标示符 */

static NSString *const kHeaderReusableViewIdentifier = @"headerReusableViewIdentifier";     /**< 头部视图格标示符 */

@interface ChannelChooseViewController ()<UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout>{

    NSMutableArray *_dataSource; /**< 数据源 */
    NSMutableArray *_AddDataSource; /**< 数据源 */

}

@property (nonatomic, strong) UICollectionView *collectionView;                     /**< 集合视图 */
@property (nonatomic, strong) UICollectionViewFlowLayout *collectionViewFlowLayout; /**< 流式布局 */
@property (nonatomic, strong) UILongPressGestureRecognizer *longGesture;

@end

@implementation ChannelChooseViewController

- (void)viewDidLoad {

    [super viewDidLoad];

    [self initializeDataSource];

    [self initializeUserInterface];

}

- (void)initializeDataSource {

    _dataSource = [NSMutableArray array];

    NSArray *titleArr = @[@"热点",@"视频",@"成都",@"社会",@"娱乐",@"问答",@"图片",@"历史",@"科技",@"体育",@"财经",@"军事",@"国际",@"段子",@"趣图",@"特卖",@"健康",@"房产",@"街拍"];

    for (int i = 0; i < titleArr.count; i++) {

        [_dataSource addObject:[NSString stringWithFormat:@"%@", titleArr[i]]];

    }

    _AddDataSource = [NSMutableArray array];

    NSArray *AddtitleArr = @[@"+汽车",@"+美图",@"+小说",@"+时尚",@"+育儿",@"+直播",@"+搞笑",@"+数码",@"+美食",@"+养生",@"+电影",@"+手机",@"+旅游",@"+宠物",@"+情感",@"+家居",@"+教育",@"+文化",@"+动漫"];

    for (int i = 0; i <AddtitleArr.count ; i++) {

         [_AddDataSource addObject:[NSString stringWithFormat:@"%@", AddtitleArr[i]]];

    }

}

- (void)initializeUserInterface {

    //添加
    [self.view addSubview:self.collectionView];

    //添加长按手势
    _longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(respondsToGesture:)];
    //
    [self.collectionView addGestureRecognizer:_longGesture];

}

#pragma mark -  respondsToGesture methods

- (void)respondsToGesture:(UILongPressGestureRecognizer *)gesture {

    //获取手势的变化
    switch (gesture.state) {
        case UIGestureRecognizerStateBegan: {

//            NSLog(@"手势开始");
            //获取手势在集合视图上的位置(indexPath)
            NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:[gesture locationInView:self.collectionView]];
            //开始移动,根据indexPath路径
            [self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];

        }
            break;
        case UIGestureRecognizerStateChanged: {

//            NSLog(@"手势正在执行");
            //刷新位置
            [self.collectionView updateInteractiveMovementTargetPosition:[gesture locationInView:self.collectionView]];

        }
            break;
        case UIGestureRecognizerStateEnded: {

//            NSLog(@"手势结束");
            [self.collectionView endInteractiveMovement];

        }
            break;
        default: {

//            NSLog(@"取消");
            [self.collectionView cancelInteractiveMovement];
        }
            break;
    }
}

//设置组数
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {

    return 2;
}

//设置单元格数量
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {

    if (section == 0) {

        return _dataSource.count;

    }else{

        return _AddDataSource.count;
    }

}

//配置单元格
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    //item重用
    ChannelCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kCollectionViewCellIdentifier forIndexPath:indexPath];

    if (indexPath.section == 0) {

        cell.displayLabel.text = _dataSource[indexPath.row];

    }else{

        cell.displayLabel.text = _AddDataSource[indexPath.row];

    }

    return cell;

}

- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {

    //重用
    ZWHeaderCollectionReusableView *headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kHeaderReusableViewIdentifier forIndexPath:indexPath];

    if (indexPath.section == 0) {

        headerView.displayLabel.text = @"我的频道";

    }else{

        headerView.displayLabel.text = @"频道推荐";

    }

    headerView.backgroundColor = [UIColor whiteColor];

    return headerView;

}

//设置能否移动
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {

    return YES;
}

//点击
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{


    if (indexPath.section == 0) {

        //点击第一组(我的频道)
        id objc = [_dataSource objectAtIndex:indexPath.item];
        [_dataSource removeObject:objc];
        [_AddDataSource insertObject:[NSString stringWithFormat:@"+%@",objc] atIndex:0];

        NSIndexPath *EndIndexPath = [NSIndexPath indexPathForRow:0 inSection:1];
        //移动到“频道选择”的第一个。
        [self.collectionView moveItemAtIndexPath:indexPath toIndexPath:EndIndexPath];

        //移动过后,改变字符串标题.移动item并不会改变cell标题上的文字。
        ChannelCollectionViewCell *Endcell = (ChannelCollectionViewCell *)[collectionView cellForItemAtIndexPath:EndIndexPath];

        Endcell.displayLabel.text = [NSString stringWithFormat:@"+%@",objc];

    }else{

        //点击第二组 (频道推荐)。
        id objc = [_AddDataSource objectAtIndex:indexPath.item];
        [_AddDataSource removeObject:objc];
        [_dataSource addObject:[objc substringFromIndex:1]];

        //移动到”我的频道“的最后一个。
        NSIndexPath *EndIndexPath = [NSIndexPath indexPathForRow:_dataSource.count-1 inSection:0];

        [self.collectionView moveItemAtIndexPath:indexPath toIndexPath:EndIndexPath];

        //移动过后,改变字符串标题。移动item并不会改变cell标题上的文字。
        ChannelCollectionViewCell *Endcell = (ChannelCollectionViewCell *)[collectionView cellForItemAtIndexPath:EndIndexPath];

        Endcell.displayLabel.text = [objc substringFromIndex:1];

    }

}

//在里面处理移动的逻辑(从哪里移动到哪里)
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {

#pragma mark - 三种情况,在第一组移动,在第二组移动,跨组移动。
    if (sourceIndexPath.section == destinationIndexPath.section) {

        //同组
        if (sourceIndexPath.section == 0) {

            //同组中的第一组
            //取出源item数据
            id objc = [_dataSource objectAtIndex:sourceIndexPath.item];
            //从资源数组中移除该数据
            [_dataSource removeObject:objc];
            //将数据插入到资源数组中的目标位置上
            [_dataSource insertObject:objc atIndex:destinationIndexPath.item];

        }else{

            //同组中的第二组
            //取出源item数据
            id objc = [_AddDataSource objectAtIndex:sourceIndexPath.item];
            //从资源数组中移除该数据
            [_AddDataSource removeObject:objc];
            //将数据插入到资源数组中的目标位置上
            [_AddDataSource insertObject:objc atIndex:destinationIndexPath.item];

        }

    }else{

       //跨组,从第一组移动到第二组,从第二组移动到第一组
        if (sourceIndexPath.section < destinationIndexPath.section) {

            //获取到这个item
            ChannelCollectionViewCell *Endcell = (ChannelCollectionViewCell *)[collectionView cellForItemAtIndexPath:destinationIndexPath];

            //从第一组拖到第二组
            id objc = [_dataSource objectAtIndex:sourceIndexPath.item];
            [_dataSource removeObject:objc];
            [_AddDataSource insertObject:[NSString stringWithFormat:@"+%@",objc] atIndex:destinationIndexPath.item];

            //移动过后,改变字符串标题.移动item并不会改变cell标题上的文字。
            Endcell.displayLabel.text = [NSString stringWithFormat:@"+%@",objc];

        }else{

            //获取到这个item
            ChannelCollectionViewCell *Endcell = (ChannelCollectionViewCell *)[collectionView cellForItemAtIndexPath:destinationIndexPath];

            //从第二组拖到第一组
            id objc = [_AddDataSource objectAtIndex:sourceIndexPath.item];
            [_AddDataSource removeObject:objc];
            [_dataSource insertObject:[objc substringFromIndex:1] atIndex:destinationIndexPath.item];

            //移动过后,改变字符串标题.移动item并不会改变cell标题上的文字。
            Endcell.displayLabel.text = [objc substringFromIndex:1];

        }

    }

}

#pragma mark -  Getter methods

- (UICollectionViewFlowLayout *)collectionViewFlowLayout {

    if (!_collectionViewFlowLayout) {

        _collectionViewFlowLayout = [[UICollectionViewFlowLayout alloc] init];
        //设置单元格的大小
        _collectionViewFlowLayout.itemSize = CGSizeMake((SCREEN_W-50)/4,50);
        //设置每行之间的最小间距
        _collectionViewFlowLayout.minimumLineSpacing = 10;
        //设置单元格之间的最小间距
        _collectionViewFlowLayout.minimumInteritemSpacing = 10;
        //设置滚动方向 Direction(方向) Vertical(垂直纵向)
        _collectionViewFlowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
        //设置头部视图的高度
        _collectionViewFlowLayout.headerReferenceSize = CGSizeMake(CGRectGetWidth(self.view.bounds), 54);
        //设置头部视图在超出屏幕范围过后是否保留(ios9新特性)
        _collectionViewFlowLayout.sectionHeadersPinToVisibleBounds = NO;

    }

    return _collectionViewFlowLayout;

}

- (UICollectionView *)collectionView {

    if (!_collectionView) {

        //初始化集合视图
        _collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 24, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds) - 24) collectionViewLayout:self.collectionViewFlowLayout];
        //设置每组的内部偏移
        _collectionViewFlowLayout.sectionInset = UIEdgeInsetsMake(0, 10, 10, 10);
        //设置数据源
        _collectionView.dataSource = self;
        //设置代理
        _collectionView.delegate   = self;
        //修改背景颜色
        _collectionView.backgroundColor = COLOR_RGB(255, 255, 255);
        //设置是否多选
        _collectionView.allowsMultipleSelection = NO;
        //注册单元格
        [_collectionView registerClass:[ChannelCollectionViewCell class] forCellWithReuseIdentifier:kCollectionViewCellIdentifier];

        //注册头部视图
        //注册尾部用UICollectionElementKindSectionFooter
        [_collectionView registerClass:[ZWHeaderCollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:kHeaderReusableViewIdentifier];

        //隐藏滚动轴
        _collectionView.showsVerticalScrollIndicator = NO;

    }
    return _collectionView;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值