最近在項目中遇到這樣一個有關iOS手勢的問題,首先需求描述如下:“在一個CollectionView中,要求長按不同的cell,產生一個cell的snapshot,此時可拖拽這個snapshot再進行后續的操作(如拖到view的某個位置出發一個事件)”。需求本身並不復雜,但要求每次只能有一個cell響應長按手勢,不允許同時有兩個或以上的cell響應長按手勢。
我們知道UIGestureRecognizer有很多回調和方法可以兼容同一個View上的多種手勢,網上相關的教程也很多,比如:
http://coder.aqualuna.me/2011/07/uigesturerecognizer.html
也有這種很全面的流水式教程:
http://blog.csdn.net/namehzf/article/details/7424882
但是都沒有解決我的問題的方案,因為我研究的是多個subview上的手勢共存問題,不是單個view上不同手勢共存問題。於是求人不如求自己,自己動手解決吧。(為此我做了個簡化的demo放在這里https://github.com/pigpigdaddy/LongPressSingleDemo,供讀者下載斧正)
思路有兩條:
1,將長按手勢加在collectionView上,通過手勢在collectionView上的位置point,獲取此位置的cell(如果有)。優勢:代碼量少,邏輯簡單。劣勢:兩個cell同時長按時,任何一個長按操作都無法識別!
相關代碼:
1 - (void)longPressAction:(UILongPressGestureRecognizer *)ges 2 { 3 if (ges.state == UIGestureRecognizerStateBegan) { 4 CGPoint point = [ges locationInView:self.collectionView]; 5 NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:point]; 6 NSLog(@"%@==%d==%d",indexPath, indexPath.section, indexPath.row); 7 } 8 }
既然思路1無法實現我的需求,那么我就不就此展開討論,demo代碼中也沒有體現,不過這種思路或許可以適用於其它需求。
2,將長按手勢加在collectionViewCell上,長按響應后,以delegate回調的形式,將所長按cell的location、cell的index等相關屬性上傳到上層,再進行相關后續操作。優勢:可以同時響應一個或者多個cell的長按。劣勢:多個cell同時響應,無法滿足我項目的需求。於是,如何解決呢?這才是這篇文章的關鍵。
UIGestureRecognizer的回調中,有一個最基本的:
1 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 2 { 3 return YES;
4 }
默認返回YES。那么,如果我在當前cell響應了長按事件后,將這里設置為NO,那么接下來的cell就不在響應長按事件了。另外,當前cell的長按結束后,再將這里設置為YES,后續的cell就又能正常響應自己的長按事件了。
於是,我創建了一個單例,專門用於控制全局的長按事件,之所以做成單例,而不在cell的上層view來記錄長按狀態,因為考慮到項目之間的通用性,以后項目如果需要這樣的需求,可以直接引用這個單例類。
OK,下面是我的單例的接口文件:
1 // 2 // LongPressControl.h 3 // Classroom 4 // 5 // Created by pigpigdaddy on 14-3-24. 6 // Copyright (c) 2014年 pigpigdaddy. All rights reserved. 7 // 8 // 某些界面上可能會有多個子界面,這些子界面都有自己的長按事件,如果你不想讓這些長按事件先后都觸發 9 // 可以使用本類加以控制 10 11 // 2014-04-08 增加區分手勢調用 12 13 typedef enum { 14 LONG_PRESS_VIEW_DEMO = 1, 15 }LONG_PRESS_VIEW; 16 17 #import <Foundation/Foundation.h> 18 19 @interface LongPressControl : NSObject 20 { 21 NSMutableArray *_arrayLongPressView; 22 } 23 24 #pragma mark 25 #pragma mark-------------創建 銷毀--------------------- 26 /** 函數名稱 :shareInfo 27 ** 函數作用 :創建 LongPressControl 單例對象 28 ** 函數參數 : 29 ** 函數返回值:URLog 單例對象 30 **/ 31 +(LongPressControl *)shareInfo; 32 33 /** 函數名稱 :freeInfo 34 ** 函數作用 :釋放 LongPressControl 單例對象 35 ** 函數參數 : 36 ** 函數返回值: 37 **/ 38 +(void)freeInfo; 39 40 /*! 41 * TODO:添加長按事件 42 * 43 * @param view 調用的view 44 * 45 * @author pigpigdaddy 46 */ 47 - (void)addLongPressAction:(LONG_PRESS_VIEW)view; 48 49 /*! 50 * TODO:刪除長按事件 51 * 52 * @param view 調用的view 53 * 54 * @author pigpigdaddy 55 */ 56 - (void)removeLongPressAction:(LONG_PRESS_VIEW)view; 57 58 /*! 59 * TODO:是否存在長按事件 60 * 61 * @param view 是那個View 62 * 63 * @return 64 * 65 * @author pigpigdaddy 66 */ 67 - (BOOL)isExistLongPressAction:(LONG_PRESS_VIEW)view; 68 69 @end
LONG_PRESS_VIEW這個Type,用於記錄並區分當前是哪個view需要這樣的控制,比如這個demo中就是LONG_PRESS_VIEW_DEMO,以后可以在類型中添加新的type。
_arrayLongPressView用於將Type類型添加進來,如果cell手勢響應,就添加相應view的type進來。
三個函數,分別在cell開始手勢時添加type、cell結束手勢時移除type、以及判斷當前view是否已經有cell正在被響應手勢。
這樣,這個單例就像是一把鎖,一旦手勢進入就上鎖,直到手勢結束才打開鎖。
來看一下我實際的democell的實現文件,如何處理手勢
1 - (void)longPressAction:(UILongPressGestureRecognizer *)ges 2 { 3 switch (ges.state) { 4 case UIGestureRecognizerStateBegan:{ 5 NSLog(@"%@",[NSString stringWithFormat:@"%d===%d", self.indexPath.section, self.indexPath.row]); 6 } 7 break; 8 case UIGestureRecognizerStateChanged:{ 9 10 } 11 break; 12 case UIGestureRecognizerStateEnded:{ 13 [[LongPressControl shareInfo] removeLongPressAction:LONG_PRESS_VIEW_DEMO]; 14 } 15 break; 16 case UIGestureRecognizerStateCancelled:{ 17 [[LongPressControl shareInfo] removeLongPressAction:LONG_PRESS_VIEW_DEMO]; 18 } 19 break; 20 case UIGestureRecognizerStateFailed:{ 21 [[LongPressControl shareInfo] removeLongPressAction:LONG_PRESS_VIEW_DEMO]; 22 } 23 break; 24 25 default: 26 break; 27 } 28 } 29 30 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 31 { 32 if (gestureRecognizer != self.longPressGes) { 33 return NO; 34 }else if ([[LongPressControl shareInfo] isExistLongPressAction:LONG_PRESS_VIEW_DEMO]){ 35 return NO; 36 }else{ 37 [[LongPressControl shareInfo] addLongPressAction:LONG_PRESS_VIEW_DEMO]; 38 return YES; 39 } 40 }
如此,我的collectionView中的多個cell,不再同時響應多個cell的手勢,而且同時長按時,也只有第一個cell會響應長按手勢。工作良好!
請參考demo(這里https://github.com/pigpigdaddy/LongPressSingleDemo)
另外,我遇到一個問題,就是
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 函數每次進入的時候,都會有一個仿佛“collectionViewCell自帶的長按手勢(_handleGestureRecognizer)”會進入,我參看了UICollectionViewCell的.h文件,其中聲明了@class UILongPressGestureRecognizer;但並沒有發現有任何關於手勢的接口,很奇怪,是iOS的保留節目嗎?有知道的可以幫忙解釋一下嗎?感謝!