目录
前言
可用来:
1. 扩大按钮的点击区域
2.
用户操作设备的方式:触摸屏幕、晃动设备、通过遥控设施控制设备。对应的事件类型有以下三种:
触屏事件(Touch Event)
运动事件(Motion Event)
远端控制事件(Remote-Control Event)
- 响应者链(即响应链)
由当前响应者向父响应者以上所延伸出的一条链(view->superView…->VC->Window->Appliction)
1、响应者(有响应和处理事件能力的对象)直接或间接继承自UIResponder
2、第一响应者:最先响应事件的响应者
3、当前响应者:当前负责响应事件的响应者
- 触摸操作的响应处理过程
1、寻找第一响应者
当iOS系统检测到触摸操作时,会将触摸事件打包成UIEvent对象并存入当前UIApplication应用的事件队列中。UIApplication将触摸事件从事件队列取出,并传递给UIWindow。
UIWindow会调用根视图的hitTest:withEvent:方法,该方法内部会调用pointInside:withEvent:判断触摸点是否在根视图内;
若不在,则hitTest:withEvent:返回nil;
若在,则向当前视图的所有子视图(subviews,index越大越先被访问,所有子视图的遍历顺序是从最顶层视图一直到到最底层视图,即从subviews数组的末尾向前遍历)发送hitTest:withEvent:消息。若子视图还有子辈视图且触摸点在该子视图(当前视图)内则重复上步操作。直到有出现以下2种临界情况,则找到第一响应者,处理结束。
/*
直到2种临界情况
1、子视图(且该子视图没有子辈视图)的pointInside:withEvent:方法返回true,hitTest:withEvent:方法返回此对象,处理结束。
2、子视图(且该子视图所有子辈视图pointInside:withEvent:方法返回false即hitTest:withEvent:返回nil)的pointInside:withEvent:方法返回true,则hitTest:withEvent:方法返回此对象,处理结束。
*/
2、向上分发
当前响应者可以选择处理或者忽略响应。选择忽略则交给父节点去响应,选择处理则可选择是否向父节点传递事件(默认:停止分发) 。如果最后交给UIApplication也没处理该事件,则忽略该事件。
例子:如果一个View是UIScrollView的子View,触摸view上下滑动会使UIScrollView滚动。如果一个View只是在UIScrollView的上方,并不是UIScrollView的子View,则触摸view上下并不会使UIScrollView滚动。
返回下一个响应者(即父响应者)
[button nextResponder]
例
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
UIView *touchView = self;
if ([self pointInside:point withEvent:event] &&
(!self.hidden) &&
self.userInteractionEnabled &&
(self.alpha >= 0.01f)) {
for (UIView *subView in self.subviews) {
[subview convertPoint:point fromView:self];
UIView *subTouchView = [subView hitTest:subPoint withEvent:event];
if (subTouchView) {
touchView = subTouchView;
break;
}
}
} else {
touchView = nil;
}
return touchView;
}
- hitTest:withEvent:会忽略哪些视图
1、隐藏(hidden=YES)的视图
2、禁止用户操作(userInteractionEnabled=false、或 enable=false)的视图
3、以及alpha级别小于0.01(alpha<0.01)的视图
4、子视图超过父视图的区域
- hitTest:withEvent:的应用
1. 扩大按钮的点击范围
2. 重叠时希望下方View响应事件
示例1: 扩大按钮的点击范围
继承:ZYDMatchPraiseButton即可
@interface ZYDMatchPraiseButton : UIButton
@end
@implementation ZYDMatchPraiseButton
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
CGRect rect=self.bounds;
CGFloat width=rect.size.width;
CGFloat height=rect.size.height;
rect=CGRectInset(rect, -width*0.5, -height*0.5); // 宽高各扩大了一半
return CGRectContainsPoint(rect, point);
}
/*
// 另一种,按固定大小
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event {
return CGRectContainsPoint(HitTestingBounds(self.bounds, 100, 100), point);
}
CGRect HitTestingBounds(CGRect bounds, CGFloat minimumHitTestWidth, CGFloat minimumHitTestHeight) {
//
CGRect hitTestingBounds = bounds;
if (minimumHitTestWidth > bounds.size.width) {
hitTestingBounds.size.width = minimumHitTestWidth;
hitTestingBounds.origin.x -= (minimumHitTestWidth - bounds.size.width)/2;
}
if (minimumHitTestHeight > bounds.size.height) {
hitTestingBounds.size.height = minimumHitTestHeight;
hitTestingBounds.origin.y -= (minimumHitTestHeight - bounds.size.height)/2;
}
return hitTestingBounds;
}
*/
@end