KylinBL的专栏

谋定而后动,三思而后行。

ios uitableview里点击界面退出键盘的实现和一些疑惑

看起来蛮简单的功能,给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 cancelsTouchesInView
Discussion

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:


大概意思是说:在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之前的版本处理效果,所以两者都执行了。


这只是现阶段我做出的能让我暂时认可的解释,如果有误或者哪位高人有更好的见解,请不吝赐教。




阅读更多
个人分类: iphone开发
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭