iOS-界面相关iOS开发iOS Developer

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

2017-04-08  本文已影响734人  o翻滚的牛宝宝o

前言


我们的产品突然提出一个需求,希望让用户更快地选择照片,通过滑动的方式而不是一张一张点击选择,并且给了我们一个参考对象,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中的位置,然后进一步比较,判断选择不选择。

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

选中 & 不选中

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

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

区域选中

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

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

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

区域判断

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代表不滑动。这样,滑动的动画就可以用公式表示出来。

这样做的好处是可以用一个变量就控制上下滑动,还能适当改变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也是我第一次尝试,如果有什么更好方式或者效率更高的改进,欢迎在评论区提出来~

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

上一篇下一篇

猜你喜欢

热点阅读