UITableView 与 UITapGesture 的一点疑惑与探讨

关键词

UITableView UITapGesture 点击空白收起键盘 触摸 点击 事件分发

描述

在当前页面可编辑状态下,点击空白处取消可编辑状态,收起键盘的功能非常常见。伴随着该功能,可能扩展出键盘出现,调整视图Frame, 可滑动,自动上移等一系列功能。

问题

UITableView中使用 UITapGesture冲突,表现奇怪

首先,实现点击空白处收起键盘功能,看起来蛮简单,只需要以下代码

    UITapGestureRecognizer *hideKeyboardGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideKeyBoard:)];
    [self.tableView addGestureRecognizer:hideKeyboardGesture];
- (void)hideKeyBoard:(UIGestureRecognizer *)gestureRecognizer
{
    [self.tableView endEditing:YES];
}

那么问题来了

  1. 普通Cell(只包含Label,View,UIImageView)快速点击时,触发了Tap手势

  2. 普通Cell(只包含Label,View,UIImageView),稍微重一点点击时,触发了Tap手势,并且还触发了点击视觉效果,即setHighlighted方法,但没有触发didSelectRowAtIndexPath方法

  3. 普通Cell(只包含Label,View,UIImageView),长按,没有触发了Tap手势,触发了点击视觉效果,即setHighlighted方法,也触发了didSelectRowAtIndexPath方法

  4. 特殊Cell(包含UIButton,UITextField),不会触发Tap手势,也不会触发Cell点击效果

要解决以上问题,先明确一些背景知识

  • 点击事件的分发:点击事件由UIApplication分发到当前触发点击事件的View,然后一直向上传递至最顶层的View,其中任何一环接受并处理了这个点击事件则停止,或者没有任何环节处理则直接静默不处理。至于UIApplication是如何分发到当前触发点击事件的View,是通过HitTest递归寻找到目标的。
    具体参考转帖:http://blog.csdn.net/sakulafly/article/details/18766339

  • 点击事件与TapGesture:Gesture Recognizers可能会延迟将触摸事件发送到hit-test view上,默认情况下,当Gesture Recognizers识别到手势后,会向hit-test view发送cancel消息,来取消之前发给hit-test view的事件
    具体参考转帖:http://blog.csdn.net/chun799/article/details/8194893

  • 请看苹果文档下面这段话

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.
If you have a custom subclass of one of these controls and you want to change the default action, attach a gesture recognizer directly to the control instead of to the parent view. Then, the gesture recognizer receives the touch event first. As always, be sure to read the iOS Human Interface Guidelines to ensure that your app offers an intuitive user experience, especially when overriding the default behavior of a standard control.

分析

  • 当给UITableView设置了UITapGesture以后,所有UITableView上的符合UITapGesture的点击事件都被UITapGestureRecognizer获取。解决了问题1

  • 长按不符合UITapGesture的要求。解决了问题3

  • 背景知识3,解决了问题4

  • 对于问题2,可能是点击事件触发了UITapGestureRecognizer,但是cancel消息是延迟发出的,在消息送达前,Cell已经完成了点击事件的接收与处理,触发了setHighlighted方法设置了highlighted状态,但是cancel事件拦截了didSelectRowAtIndexPath

进一步分析,如何解决问题2的怪异现象

public var cancelsTouchesInView: Bool 
// default is YES. causes touchesCancelled:withEvent: to be sent to the view for all touches recognized as part of this gesture immediately before the action method is called

public var delaysTouchesBegan: Bool 
// default is NO.  causes all touch events to be delivered to the target view only after this gesture has failed recognition. set to YES to prevent views from processing any touches that may be recognized as part of this gesture

public var delaysTouchesEnded: Bool 
// default is YES. causes touchesEnded events to be delivered to the target view only after this gesture has failed recognition. this ensures that a touch that is part of the gesture can be cancelled if the gesture is recognized
  • cancelsTouchesInView属性表示是否发送touchesCancelled事件。

  • delaysTouchesBegan默认NO和delaysTouchesEnded默认YES正是问题2的根源,它导致cell接收到了TouchesBegan点击事件产生了点击效果,但是End事件却没有接收到从而无法调起didSelectRowAtIndexPath

  • delaysTouchesBegan设为Yes,则不会产生问题2这样奇怪的现象

  • 但是如此解决问题,导致点击Cell只会触发UITapGestureRecognizer,无法点击Cell Item

解决方案

方法1:

// Generally, all responders which do custom touch handling should override all four of these methods.
// Your responder will receive either touchesEnded:withEvent: or touchesCancelled:withEvent: for each
// touch it is handling (those touches it received in touchesBegan:withEvent:).
// *** You must handle cancelled touches to ensure correct behavior in your application.  Failure to
// do so is very likely to lead to incorrect behavior or crashes.

public func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
public func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?)
public func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?)
public func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?)
  • 重载touchesBegan方法,在该方法里添加键盘事件,但是该方法全部统一对待,在自定义程度上较低,而且系统也不太建议

方法2:

// called before touchesBegan:withEvent: is called on the gesture recognizer for a new touch. return NO to prevent the gesture recognizer from seeing this touch

@available(iOS 3.2, *)
optional public func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool
  • 使用UIGestureRecognizerDelegate的shouldReceiveTouch方法,在接收到Touch事件时,通过逻辑去判断当前点击主体,从而控制是否需要接收该Touch事件

方法3:

let bkgView = UIView(frame: UIScreen.mainScreen().bounds)
bkgView.backgroundColor = UIColor.clearColor()
bkgView.userInteractionEnabled = true
bkgView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "bkgOnClick:"))
tableView.insertSubview(bkgView, atIndex: 0)
  • 此方法在ScollView中可以完美使用,但是在UITableView中使用时,SectionHeader和SectionFooter无法触发手势

以上是笔者想到的方法,欢迎拍砖,如果朋友有什么更好更简便的方法,请评论告诉我,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值