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閱讀 230,431評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,604評論 2 380

推薦閱讀更多精彩內容

  • 前言 iOS里的UI控件其實沒有幾個,界面基本就是圍繞那么幾個控件靈活展開,最難的應屬UICollectionVi...
    alenpaulkevin閱讀 31,983評論 9 176
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,176評論 4 61
  • 最近將 UICollectionView 進行了一個全面的學習及總結,參考了網上大量的文章,把官方文檔進行了大概翻...
    varlarzh閱讀 21,528評論 3 94
  • 人生很短,愿你遇見,所有的美好,日子很長,愿你深情不會被辜負 001 最高級的成熟在于自身修養 其實成熟無關年齡的...
    橙琳媽閱讀 604評論 0 1
  • 枯木泣城門,驚心憶浮沉, 不為三兩語,許作一心人。
    荒漠香果海閱讀 291評論 0 0