UICollectionView & UICollectionDelegate 相关探索

视觉

UICollectionView与UITableView十分的类似,但是UICollection的功能更加的强大,其强大之处在与UICollectionViewLayout,这个类的作用是给cell提供布局信息。因此,不管什么样的布局,都可以通过UICollectionViewLayout实现。而本文主要说说UICollectionView的一些相关使用,如:cell的移动和cell的自定义编辑菜单,还有布局的变化(即CollectionView中Layout对象的改变)。

1.cell自定义编辑菜单

Simulator Screen Shot 2016年5月22日 15.13.28.png

如上图所示,其中的copy、custom按钮对应的菜单就是cell自带的编辑菜单。当我们长按cell中对应的某个项目的时候,就会弹出编辑菜单。编辑菜单是一个UIMenuController单例,而其中的item对应的是UIMenuItem。要实现编辑菜单也十分的简单,只需要按照如下的步骤即可:

第一步:实现如下三个代理方法

/**
 *  当我们长按cell的时候会调用此方法,告知是否可以显示编辑菜单
 */
- (BOOL)collectionView:(UICollectionView *)collectionView shouldShowMenuForItemAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

/**
 *  此方法需实现,告知我们需要显示的item(即是否显示copy、cut等item,在ios7之后是在cell中告知的)
 */
- (BOOL)collectionView:(UICollectionView *)collectionView canPerformAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
    return NO;
}

/**
 *  此方法也需实现,但实现类容可无
 */
- (void)collectionView:(UICollectionView *)collectionView performAction:(SEL)action forItemAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender {
}

第二步:定制自己的item,本步骤可有可无,全凭是否添加自定义item

// 实例化一个item
UIMenuItem * customItem = [[UIMenuItem alloc] initWithTitle:@"Custom" action:@selector(customItem:)];
// 添加到UIMenuController中去
[[UIMenuController sharedMenuController] setMenuItems:@[customItem]];

注意:此处的selector对应的方法必须放在自定义cell的实现文件中。

第三步:cell中添加必须的方法

// 此方法保证UIMenuController能称为第一响应者,能被显示出来
- (BOOL)canBecomeFirstResponder {
    return YES;
}

// 这里决定要显示的item,在这里可以过滤到系统的item
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    // 显示系统自带的copy item
    if (action == @selector(copy:)) {
        return YES;
    }
    
    if (action == @selector(customItem:)) {
        return YES;
    }
    
    return NO;
}

- (void)customItem:(UIMenuController *)sender {
}

- (void)copy:(UIMenuController *)sender {
}

至此,自定义cell的编辑菜单就算完成了,当然点击item需要执行的操作应该控制器在完成,这里在selector方法中应该通过代理回到控制器中去完成。

2.Layout Object 的改变

首先我们来看看效果

2016-05-22 16_02_50.gif

Layout Object对象的改变有两种方式,第一种是通过代码方式,这种方法简单,可以带有动画,但是不能实现动画的进度控制。第二种方式是一种基于手势的改变LayoutObject对象,这种方式对应的动画进度是可控的。但是,在改变LayoutObject对象之前,要确保你有两种布局方式,即两个Layout对象。

本文采用的是自定义的两个布局对象为:环形布局、堆叠式布局

首先看一看第一种方式:

- (IBAction)changeItem:(id)sender {
    static BOOL isStack = NO;
    isStack = !isStack;
    if (isStack) {
        [self.collectionView setCollectionViewLayout:self.stackLayout animated:YES];
    } else {
        [self.collectionView setCollectionViewLayout:self.cycleLayout animated:YES];
    }
}

这里,当我点击按钮的时候,布局就动画的从cycleLayout布局变化到了stackLayout布局,在此点击就换回来了。第一种方法的实现就完成了。

接下来看看第二种方式:

看看动画

2016-05-22 16_22_55.gif

首先给UICollectionView添加一个屏幕边缘手势:

UIScreenEdgePanGestureRecognizer * edgePan = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgePan:)];
    edgePan.edges = UIRectEdgeLeft;
    [self.view addGestureRecognizer:edgePan];

其次实现手势响应方法

- (void)edgePan:(UIScreenEdgePanGestureRecognizer *)edgePan {
    
    // 计算拖拽的进度
    CGFloat progress = [edgePan translationInView:self.view].x / 200;
    progress = MIN(1, MAX(0, progress));
    
    if (edgePan.state == UIGestureRecognizerStateBegan) { // 是手势开始
        // 告知collection开始交互变化Layout,以及交互变化layout完成调用的block
        self.transitionLayout = [self.collectionView startInteractiveTransitionToCollectionViewLayout:self.stackLayout completion:^(BOOL completed, BOOL finished) {
            NSLog(@"完成");
        }];
    } else if (edgePan.state == UIGestureRecognizerStateChanged) { // 如果是手势进行中,将进度告知collection的开始时返回的对象
        
        self.transitionLayout.transitionProgress = progress;
        [self.transitionLayout invalidateLayout];
    }  else if (edgePan.state == UIGestureRecognizerStateEnded) { // 如果是手势结束
        if (progress < 0.5) { // 如果完成进度小于0.5,就取消本次layout对象的转变
        
        [self.collectionView cancelInteractiveTransition];
    } else {
        
        [self.collectionView finishInteractiveTransition];
    }
    }
}

当我们的手指从屏幕边缘划过的时候,如果是刚开始滑动,就告知UICollectionView开始交互变化布局,调用UICollectInView的startInteractiveTransitionToCollectionViewLayout:方法,此方法会返回一个系统提供的用于变化布局的对象,将此对象保存下来。当滑动进行中,我们通过滑动的距离和规定的滑动距离向比较,得出滑动完成的进度,并把进度告知系统提供的用于变化布局的对象。当滑动完成时,通过滑动完成进度与0.5的比较告知是否完成交互变化布局。

3.cell的手势交互移动

不多说,还是先看看效果:

2016-05-22 16_46_28.gif

直接上代码:

第一步:给cell添加tap手势

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    
    KVCollectionViewCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellReuseIdentifier forIndexPath:indexPath];
    cell.indexLabel.text = [NSString stringWithFormat:@"我是国民好男人%ld", indexPath.row];
    
    // 给cell添加手势
    UIPanGestureRecognizer * panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [cell addGestureRecognizer:panGesture];
    return cell;
}

第二步:实现手势响应方法

- (void)pan:(UIPanGestureRecognizer *)pan {
    
    UIView * view = pan.view;
    
    NSIndexPath * indexPath = [self.collectionView indexPathForCell:(KVCollectionViewCell *)view];
    
    CGPoint curentPoint = [pan locationInView:self.collectionView];
    
    if (pan.state == UIGestureRecognizerStateBegan) { // 手势开始
        // 告知collectionView开始交互移动
        [self.collectionView beginInteractiveMovementForItemAtIndexPath:indexPath];
    } else if (pan.state == UIGestureRecognizerStateChanged) { // 手势进行
        // 告知collectionView更新交互移动
        [self.collectionView updateInteractiveMovementTargetPosition:curentPoint];
    } else if (pan.state == UIGestureRecognizerStateCancelled) { // 手势取消
        // 告知collectionView交互移动结束
        [self.collectionView cancelInteractiveMovement];
    } else if (pan.state == UIGestureRecognizerStateEnded) { // 手势结束
        // 告知collectionView交互移动结束
        [self.collectionView endInteractiveMovement];
    }
}

第三步:实现手势驱动必须的代理和数据源方法

/**
 *  数据源方法,告知特定item是否可以移动
 */
- (BOOL)collectionView:(UICollectionView *)collectionView canMoveItemAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

/**
 *  手势移动结束调动此方法,以最终确定目的地的IndexPath
 */
- (NSIndexPath *)collectionView:(UICollectionView *)collectionView targetIndexPathForMoveFromItemAtIndexPath:(NSIndexPath *)originalIndexPath toProposedIndexPath:(NSIndexPath *)proposedIndexPath {
    // 通常直接返回proposedIndexPath即可
    return proposedIndexPath;
}

/**
 *  手势移动结束,系统调用此方法告知移动的对象开始的IndexPath、移动结束的IndexPath
 */
- (void)collectionView:(UICollectionView *)collectionView moveItemAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
    // 在此处改变数据源中对应indexPath
}

本文所使用的Demo一上传至github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,811评论 1 22
  • 背景介绍 学习Swift的时候写工具类的时候突发奇想,想要使用block代替selector,尝试了很多次,最后还...
    勇不言弃92阅读 554评论 0 0