iOS-事件的hitTest处理

因为之前项目有做超出父VIew的点击事件,所以记录一下,以后方便自己巩固

1.为什么可以触发事件

首先看一下UIView,UIVIewController,UIApplication的继承,都有UIResponder这个类,大家都叫它响应者对象,那为什么继承它就能接收和处理事件呢?看一下他提供了哪些方法...

- (void)touchesBegan:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEstimatedPropertiesUpdated:(NSSet *)touches NS_AVAILABLE_IOS(9_1);

- (void)pressesBegan:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesChanged:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesEnded:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);
- (void)pressesCancelled:(NSSet *)presses withEvent:(nullable UIPressesEvent *)event NS_AVAILABLE_IOS(9_0);

- (void)motionBegan:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionEnded:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(3_0);

- (void)remoteControlReceivedWithEvent:(nullable UIEvent *)event NS_AVAILABLE_IOS(4_0);

这些方法应该不是很陌生,分别是触摸事件,指压事件(iOS 9 Deep press的相关方法),加速计方法(给陀螺仪和加速传感器使用的方法),远程控制(比如耳机控制)

2.事件产生和传递

事件出发后会封装成UIEvent然后放入UIApplication管理的队列中(先进先出),UIApplication会从最前面取出事件,将它分发下去,最先分发到主窗口(keyWindow),然后主窗口判断自己是否能接受触摸事件,触摸点是否在自己范围之内,如果可以的话,在子控件中由后往前遍历,判断是否合适,如果合适继续遍历当前控件的子控件,如果不合适就是自己本身喽 ,所以它的传递过程是从父控件传递给子控件的.

有三个方法判断是否接受事件

  1. userInteractionEnabled (默认view是YES ,表示接受事件)
  2. alpha 透明度 (< 0.01不接受)
  3. hidden 隐藏

3.如何实现

UIView两个重要的方法
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event (有事件传递给一个控件,控件就会触发这个方法 ,然后通过下面方法判断是否在范围内,返回NO则忽略整个view)
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
这样就可以重写方法上面的方法来实现一些功能,要想重写都要自定义View(像扩大点击事件范围,点击超过父VIew范围)

  1. 写个点击超过父VIew范围的简单例子,要重写父VIew的hitTest方法


    iOS-事件的hitTest处理_第1张图片
    黄色是父VIew 加了一个红色的子view和UILabel,想要触发红View的点击事件

#import "YellowView.h"

@implementation YellowView
- (instancetype)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor yellowColor];
        
        UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, -60, 50, 50)];
        redView.backgroundColor = [UIColor redColor];
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapaction)];
        [redView addGestureRecognizer:tap];
        [self addSubview:redView];
        
        UILabel *lbl = [[UILabel alloc]initWithFrame:CGRectMake(-60, 10, 50, 30)];
        lbl.font = [UIFont systemFontOfSize:13];
        lbl.text = @"看得出来";
        [self addSubview:lbl];
    }
    return self;
}

-(void)tapaction{
    NSLog(@"我是姨妈红");
}

-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
 
    if (!self.userInteractionEnabled || self.isHidden == YES || self.alpha <= 0.01) {
        return nil;
    }

    for (UIView *subview in [self.subviews reverseObjectEnumerator]) { //倒序遍历
        CGPoint newPoint = [subview convertPoint:point fromView:self]; //self中point转换成subview中坐标点
       // CGPoint newPoint = [subview convertPoint:point toView:self]; subview中point转换成self中坐标点
        UIView *testHitView = [subview hitTest:newPoint withEvent:event];
        if (testHitView) { 
            return testHitView; //红色子View
        }
    }
    return nil;
}

2.改变View的触发范围

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event {

//规则的矩形
    return CGRectContainsPoint(newBounds, point); // newBounds为想要的触发范围
//圆形
   return pointToCenterDistance < halfRadius; // point到圆心的距离小于半径
}

以上只是自己的了解,望多多指教

你可能感兴趣的:(iOS-事件的hitTest处理)