看起來蠻簡單的功能,給UITableView增加一個tap的手勢,響應一個方法來退出鍵盤就行了,於是很有了下面代碼:
//增加tap手勢,點擊使退出鍵盤
UITapGestureRecognizer * tapGesture = [[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(dismissKeyBoard)];
[self.tableView addGestureRecognizer:tapGesture];
隱藏鍵盤:
-(void)dismissKeyBoard{
[_textField resignFirstResponder];
}
問題來了,因為我自訂了UITableViewCell,裡面會有一些按鈕之類的控制項,如果點到這些控制項上時,鍵盤並不會退出。
查了下資料,把tap手勢的cancelsTouchesInView設定為NO:
tapGesture.cancelsTouchesInView =NO;
這樣問題就解決了,但是關於cancelsTouchesInView這個屬性的作用,還需要繼續深入學習一下。於是查看了下apple doc的說明:
cancelsTouchesInView
A Boolean value affecting whether touches are delivered to a view when a gesture is recognized.
@property(nonatomic) BOOL cancelsTouchesInViewDiscussion
When this property is YES
(the default) and the receiver recognizes its gesture, the touches of
that gesture that are pending are not delivered to the view and previously delivered touches are cancelled through a touchesCancelled:withEvent:
message
sent to the view. If a gesture recognizer doesn’t recognize its gesture or if the value of this property is NO
, the view receives all touches in the multi-touch sequence.
簡簡單單幾句英文,理解起來還真不容易,於是專門查了下這手勢識別和事件分發方面的文章,發現了一篇講得還可以的,轉載了過來:Gesture Recognizers與觸摸事件分發
看了這篇文章,有了個基本瞭解,這段英文的大至的意思也清楚了,我們知道touch事件是先交給當前的view或者此view的superview的gesture recognizers來處理的,簡單來說這個屬性就是來控制gesture recognizers能不能把touch事件給吃掉。
打個比方,你有一個按鈕已經綁定了click事件了,同時你又在這個按鈕上面加上了tap手勢識別,並設定了相應處理方法;第一次先不設定tap手勢的cancelsTouchesInView,也就是說採用預設值YES,然後點擊按鈕,tap手勢綁定的事件執行了,按鈕的click事件沒有執行到,也就是說gesture
recognizers把touch事件給cancel了,不傳到button上,所以button的click事件沒法執行;接下來設定tap手勢的cancelsTouchesInView為NO,再次點擊按鈕,發現這次兩個事件都執行了,這樣就清楚了。
但是分析一下自己的問題,情境與上面的是不太一樣的,上面實踐的情境都是在一個view裡,tap手勢也是添加在這個view裡,而我現在的情境是一個UITableView添加了tap手勢識別,然後按鈕是UITableView裡的一個subView,於是有以下情景:
情景1:當cancelsTouchesInView為YES時,我點擊按鈕會觸發按鈕的click事件,但是UITableView的手勢識別事件是沒有被觸發
情景2:當cancelsTouchesInView為NO時,這兩個事件都觸發了
這就有點奇怪,如果按照上面對gesture recognizers的理解,情景1應該會是手勢識別的方法觸發了,按鈕的click事件是不會被觸發的,因為touch已經被gesture
recognizers給cancel了,難道我理解錯了?於是又仔細apple doc裡有關手勢的說明,找到下面一段:
Interacting with Other User Interface Controls
In iOS 6.0 and later, default control actions prevent overlapping gesture recognizer behavior. For example, the default action for a button is a single tap. If you have a single tap
gesture recognizer attached to a button’s parent view, and the user taps the button, then the button’s action method receives the touch event instead of the gesture recognizer. This applies only to gesture recognition that overlaps the default action for a
control, which includes:
- A single finger single tap on a
UIButton,
UISwitch,
UIStepper,
UISegmentedControl, and
UIPageControl.
- A single finger swipe on the knob of a
UISlider, in a direction parallel to the slider.
- A single finger pan gesture on the knob of a
UISwitch, in a direction parallel to the switch.
大概意思是說:在iOS6.0及更高版本中,預設的控制項actions會阻止與之重疊的手勢識別行為。例如,一個按鈕的預設操作是single tap。如果你有一個single tap的手勢辨識器串連到一個按鈕的父視圖,使用者點擊按鈕時,是按鈕的操作方法來接收觸摸事件,而不是我們熟知的先由手勢識別來接收,這樣就幹攏了手勢識別。這僅適用於手勢識別與控制的預設操作重疊的情況,下面還列舉了幾種情況的控制例子。
好嘛,我的環境是xcode4.6.2+ios sdk 6.1,正中下懷,也就是說在ios6.0之前和ios6.0之後,就是兩種不同的情況了,
iOS 5上的一個按鈕的父視圖的UITapGestureRecognizer會干擾按鈕的click事件,而iOS6.0及更高版本中,按鈕的click事件優先順序就高了,它會干擾到父視圖的UITapGestureRecognizer識別。
又分別在iOS 5和iOS 6 sdk下面運行了程式,來做了驗證,結果一致。
既然版本不一樣,效果不同,那就得處理相容性問題,最好的解決方案就是設定UITapGestureRecognizer的cancelsTouchesInView為NO,這樣在兩個版本表現才一致。
接下來就可以分版本對這種情況做出一個大致的解釋了:
ios6之前的版本:
uitableview為superview,按鈕為subview,點擊按鈕時,touch事件先由superview上UITapGestureRecognizer接收到,進行識別處理,如果識別成功,手勢的target會執行綁定的action,若UITapGestureRecognizer的cancelsTouchesInView為yes,那麼這次的touch就被cancel掉了,不會傳遞到按鈕去,按鈕的click事件也就沒執行;若UITapGestureRecognizer的cancelsTouchesInView為NO,touch事件會傳傳遞到按鈕去,按鈕的click事件也就執行到了。
ios6以後:
如果UITapGestureRecognizer的cancelsTouchesInView為yes,點擊按鈕,touch事件的接收者為按鈕的click事件,
阻塞了UITapGestureRecognizer的接收,所以按鈕的click事件執行了,而手勢的action沒有觸發;若cancelsTouchesInView為NO,事件還是由UITapGestureRecognizer先來處理,效果同ios6之前的版本處理效果,所以兩者都執行了。
這隻是現階段我做出的能讓我暫時認可的解釋,如果有誤或者哪位高人有更好的見解,請不吝賜教。