应用与用户的交互是一个应用的灵魂。
点击、摇动、滑动、旋转等会被系统封装成UIEvent,放到事件队列里等待UIApplication去取,然后寻找响应者,找到对应的方法并执行的过程就是响应。
这里寻找最佳响应者依靠两个方法:
方法一返回最佳响应者-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
方法二判断点有没有在返回的视图范围内- -(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
调用顺序是
调用方法一,在遍历找到一个视图就通过方法二判断是否符合条件,符合就返回该视图
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
// 1.判断当前控件能否接收事件
if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
// 2. 判断点在不在当前视图内
if ([self pointInside:point withEvent:event] == NO) return nil;
// 3.从后往前遍历自己的子视图
NSInteger count = self.subviews.count;
for (NSInteger i = count - 1; i >= 0; i--) {
UIView *childView = self.subviews[I];
// 把当前控件上的坐标系转换成子控件上的坐标系
CGPoint childP = [self convertPoint:point toView:childView];
UIView *fitView = [childView hitTest:childP withEvent:event];
if (fitView) { // 寻找到最合适的view
return fitView;
}
}
// 循环结束,表示没有比自己更合适的view 就返回自身
return self;
}
注意遍历子视图是从最后添加的子视图往下遍历
如上图视图A和视图B
A、B都能响应事件。切在同一父视图上的前提下,点击中间的重叠区域,响应者是?
这就看是A先添加还是B先添加了,A先B后,根据响应链肯定是B响应
,反之则A响应
增加按钮响应范围的方法
分类里重写
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
CGRect bounds = self.bounds;
bounds = CGRectInset(bounds, -10, -10);
return CGRectContainsPoint(bounds, point);
}