Swipe-to-Select照片滑动选择实现

前言


我们的产品突然提出一个需求,希望让用户更快地选择照片,通过滑动的方式而不是一张一张点击选择,并且给了我们一个参考对象,iPhone手机相册。

一开始准备从UITouch和响应链入手,然后根据坐标各种计算。实际操作后发现工程量太大,不好实现。后来打算用UISwipeGesture,但是上下左右控制十分麻烦,不得不放弃。

最后github上找到一个类似的demo:Swipe to Select GridView,总算为该事件的实现打开思路。不过,原demo和我们的项目实际需求相差甚远,于是自己动手实现了该效果。我们先来看看效果:

2017-04-08 16_11_12.gif

demo


demo地址:https://pan.baidu.com/s/1nvBcN8l

核心思想


UIPanGestureRecognizer

其实一开始看原项目中是用UIPanGestureRecognizer手势来实现滑动定位的时候还是很吃惊的,一直以为UIPanGestureRecognizer是用来做缩放之类的手势,没想到滑动手势也能胜任。更神奇的是,如果添加到view上,而view存在UICollection,纵向滑动优先触发scrollView的上下滑动,横向滑动就触发PanGesture事件后又能纵向滑动了,不需要自己写代码控制,简直和iPhone相册一模一样。(后来根据响应链的思路想想也应该是这样。。collectionView在View的前面嘛。。)

gestureRecognizer 只要设置了最大和最小触点都是1就能识别单点滑动事件。只要响应了该手势,就能拿到UIPanGestureRecognizer对象,通过 [gestureRecognizer locationInView:collectionView]方法就能获得当前触点在collectionView中的位置,然后进一步比较,判断选择不选择。

  • UIGestureRecognizerStateBegan
  • UIGestureRecognizerStateEnded

UIPanGestureRecognizer有一个state属性,当手指触发事件的时候,state == UIGestureRecognizerStateBegan,这时就能进行一些手势开始的操作,比如标记进入滑动状态等。当手指离开屏幕的时候,state == UIGestureRecognizerStateEnded,这时进行手势结束操作等。其他时刻可以根据点的位置进行判断cell选中不选中。

选中 & 不选中

仔细分析iPhone相册cell选中不选中的实现可以发现规律:

  • 找到第一个进入区域的cell和最后一个进入区域的cell,然后将2个cell位置之间的cell改变状态

如图,我只选中红色区域,蓝色区域也跟着选中。

区域选中
  • 改变的值是第一个cell变化的值

如果第一个cell变成选中,那么后面变化的cell全都是选中。如果第一个cell变成不选中,那么后面变化的cell全都不选中。

  • cell先进入选中区域,然后离开选中区域,那么选中与否与cell进入选中区域之前保持一致

这一点就比较复杂了,也就是说手指滑动进入状态后,需要产生一个临时值来保存当前选中状态(tmpIsSelected)而不是最终选中状态(isSelected)。这时候就需要结合UIGestureRecognizerStateBeganUIGestureRecognizerStateEnded进行判断。

大致思路如下(参考demo):

  • 1、进入滑动状态,将所有model的isSelected的值赋值给tmpIsSelected,界面打钩不打钩的依据完全按照tmpIsSelected属性的值来显示。

  • 2、在滑动状态中(手指滑动),cell状态改变都修改model的tmpIsSelected值。

  • 3、结束滑动状态(手指离开),将model的tmpIsSelected的值赋值给isSelected,是否打钩都依据isSelected值显示。

区域判断

tmp2da927f9.png

知道cell选中与不选中的规则之后,我们的任务就是找到首尾两个cell的位置,然后将之间的cell的状态改变就好。首先就是要找到第一个cell。

1、查找第一个cell

第一个cell的判断比较简单,就是看触点(x,y)坐标是否落入cell的区域内。这里需要遍历collectionView.visibleCells,因为手势滑到的地方肯定在可视范围内,因此要找的cell肯定也在visibleCells里面。只要遍历一遍,找到点的区域在cell的frame里面的cell即可。记录下cell要改变的状态firstSelectedCellChoose、cell的坐标firstChooseCellRect还有cell的位置firstChooseCellIndexPath

2、查找第二个cell

第二个cell就要根据第一个cell的位置划分成5个区域:上侧、下侧、同行左侧、同行右侧、cell中,如上图所示。前4个区域都要根据坐标判断,然后遍历collectionView.visibleCells,找到最后一个满足区域的cell就是第二个cell,如果不满足,就把model的tmpIsSelected值改回isSelected的值,达到划出区域选择恢复的效果。(这里需要注意collectionView.visibleCells并不是按上到下左到右返回的,因此还需要排个序)而在cell中这个区域不可能存在第二个cell(与第一个cell重复),因此只要把collectionView.visibleCells中所有的cell的选中状态恢复即可。具体算法可以参照demo。

自动滚动的实现

tmp4b396b1a.png

在滑动触发事件中,我们还得认为的添加2个区域以实现自动上滑和自动下滑功能。

想要做到控制同步自动上滑和自动下滑功能,我们可以设置一个参数,scrollSpeed,当scrollSpeed > 0 代表下滑,当scrollSpeed < 0代表下滑,scrollSpeed == 0代表不滑动。这样,滑动的动画就可以用公式表示出来。

  • collectionView.ContentOffset.y = collectionView.ContentOffset.y + scrollSpeed;

这样做的好处是可以用一个变量就控制上下滑动,还能适当改变scrollSpeed的值加快或者减慢滑动速度。

  • 注意 滑动的时候不会触发滑动手势方法,必须自己调用处理方法。

相关代码:

- (void)startScroll{
    if (!startScroll ) {
        return;
    }
    if (scrollOperationQueue.operationCount > 1) {
        return ;
    }
    __weak typeof(self) wSelf = self;
    NSOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        if (wSelf.mainCollectionView.contentOffset.y + wSelf.mainCollectionView.frame.size.height + scrollSpeed >= wSelf.mainCollectionView.contentSize.height && scrollSpeed > 0) {
        
            [UIView animateWithDuration:0.1 animations:^{
            wSelf.mainCollectionView.contentOffset =  CGPointMake(wSelf.mainCollectionView.contentOffset.x, wSelf.mainCollectionView.contentSize.height -wSelf.mainCollectionView.frame.size.height);
            }];
        
            [wSelf stopScroll];
               return;
          }
        if (wSelf.mainCollectionView.contentOffset.y + scrollSpeed  <= 0 && scrollSpeed < 0) {
        
        [UIView animateWithDuration:0.1 animations:^{
          wSelf.mainCollectionView.contentOffset =  CGPointMake(wSelf.mainCollectionView.contentOffset.x, 0);
        }];
            [wSelf stopScroll];
            return;
        
        }
    
        [UIView animateWithDuration:0.1 animations:^{
        wSelf.mainCollectionView.contentOffset =  CGPointMake(wSelf.mainCollectionView.contentOffset.x, wSelf.mainCollectionView.contentOffset.y +scrollSpeed);
    }];
        
        [wSelf dealWithPointX:scrollPoint.x pointY:scrollPoint.y];
        scrollPoint = CGPointMake(scrollPoint.x, scrollPoint.y +scrollSpeed);
        [wSelf performSelector:@selector(startScroll) withObject:nil afterDelay:0.1];
}];
    [scrollOperationQueue addOperation:operation];   
}

ps:关于滑动这块我后来又改进了下,使用UIView的动画更加流畅

总结


说了那么多,其实有很多东西只有自己去尝试后才知道是什么意思,用文字很难表达出来。

由于这个demo也是我第一次尝试,如果有什么更好方式或者效率更高的改进,欢迎在评论区提出来~

我是翻滚的牛宝宝,欢迎大家评论交流~

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

推荐阅读更多精彩内容

  • 前言 iOS里的UI控件其实没有几个,界面基本就是围绕那么几个控件灵活展开,最难的应属UICollectionVi...
    alenpaulkevin阅读 31,234评论 9 175
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 最近将 UICollectionView 进行了一个全面的学习及总结,参考了网上大量的文章,把官方文档进行了大概翻...
    varlarzh阅读 21,276评论 3 94
  • 人生很短,愿你遇见,所有的美好,日子很长,愿你深情不会被辜负 001 最高级的成熟在于自身修养 其实成熟无关年龄的...
    橙琳妈阅读 572评论 0 1
  • 枯木泣城门,惊心忆浮沉, 不为三两语,许作一心人。
    荒漠香果海阅读 212评论 0 0