触摸事件&传递&手势&故事板小发现

本文详细介绍了iOS中UIResponder对象如何处理触控事件,包括touchesBegan、touchesMoved等方法的作用及实现方式,并深入探讨了UITouch的各种属性与方法。此外,还讲解了如何通过重写hitTest和pointInside方法解决视图间的触摸穿透问题,以及UIGestureRecognizer的手势识别机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

只有继承了UIResponder的对象才能接收并处理事件。我们称之为“响应者对象”

这里写图片描述

//一根或者多根手指开始触摸view,系统会自动调用view的下面方法
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
//一根或者多根手指在view上移动,系统会自动调用view的下面方法(随着手指的移动,会持续调用该方法)
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
//一根或者多根手指离开view,系统会自动调用view的下面方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
//触摸结束前,某个系统事件(例如电话呼入)会打断触摸过程,系统会自动调用view的下面方法
(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
//提示:touches中存放的都是UITouch对象
//加速计事件(如摇一摇功能)
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
//远程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
这里主要说一下UITouch
属性
//短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
@property(nonatomic,readonly) NSUInteger          tapCount;
//当前触摸事件所处的状态  (用于判断调用什么方法)
@property(nonatomic,readonly) UITouchPhase        phase;
//触摸产生时所处的窗口
@property(nonatomic,readonly,retain) UIWindow    *window;
//触摸产生时所处的视图
@property(nonatomic,readonly,retain) UIView      *view;

方法
// 获取手指
UITouch *touch = [touches anyObject];
//返回值表示触摸在view上的位置
//这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0))
//调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置
- (CGPoint)locationInView:(UIView *)view;
//该方法记录了前一个触摸点的位置
- (CGPoint)previousLocationInView:(UIView *)view;

下面三种情况时视图会不接受触摸事件

1.不接收用户交互
userInteractionEnabled = NO
2.隐藏
hidden = YES
3.透明
alpha = 0.0 ~ 0.01

触摸事件底层的事件传递

//事件 -> UIApplication -> 窗口,让这个窗口去寻找最合适的View
底层会调用(返回的是最适合的响应视图)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

这个底层会调用(返回YES为响应No为不响应)
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;
来返回最合适的View来响应事件

例1: 如图 按钮在黄色View下 怎么才能透过View点击按钮(除了上述三种不接受触摸事件的情况)
这里写图片描述

方法一: 重写黄色视图的hitTest方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{   
//这里经常会用到 UIView的转换坐标方法
    // 把这个点的坐标系转换成按钮的坐标系
    CGPoint btnP = [self convertPoint:point toView:self.btn];
    // 判断这个点在不在按钮上面
    if ([self.btn pointInside:btnP withEvent:event]) { // 点在按钮上
        return self.btn;
    }else{
        return [super hitTest:point withEvent:event];
    }
}

方法二: 重写
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    CGPoint btnP = [self convertPoint:point toView:self.btn];
    if ([self.btn pointInside:btnP withEvent:event]) {// 在按钮上
        return NO;
    }else{
        return [super pointInside:point withEvent:event];
    }
}

例2 当子控件超出父控件范围 则不响应事件
如下图 按钮是蓝色view’的子控件 则左右侧超出部分不响应按钮点击
解决方案 在左侧 在蓝色view里 重写hitTest方法 来返回如果点在按钮上 即使超出父控件也可以响应
这里写图片描述

UIGestureRecognizer手势识别器

使用前提 是视图 可以与用户交互例如UIImageView默认是不可交互需要设置
userInteractionEnabled = YES;
//手势识别器----UIGestureRecognizer是一个抽象类,定义了所有手势的基本行为,使用它的子类才能处理具体的手势
UITapGestureRecognizer(敲击)
UIPinchGestureRecognizer(捏合,用于缩放)
UIPanGestureRecognizer(拖拽)
UISwipeGestureRecognizer(轻扫)
UIRotationGestureRecognizer(旋转)
UILongPressGestureRecognizer(长按)

例:

//创建手势
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapLabel:)];
//给Label添加手势
        [label addGestureRecognizer:tap];
        //让label可以交互
        label.userInteractionEnabled = YES;

-(void)tapLabel:(UITapGestureRecognizer *)tap{
    //如果多个Label可以通过tap.view.tag 来区分label
    NSLog(@"%zd",tap.view.tag);
    }

注意

//  对于UISwipeGestureRecognizer清扫手势默认是向右的 可以改下边属性来改变方向
  @property(nonatomic) UISwipeGestureRecognizerDirection direction;       

  UISwipeGestureRecognizer *swipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];

    // 默认向右
    swipe.direction = UISwipeGestureRecognizerDirectionDown | UISwipeGestureRecognizerDirectionUp;

        - (void)swipe:(UISwipeGestureRecognizer *)swipe
{
//可以通过这个判断 来对特定方向进行操作
    if (swipe.direction == UISwipeGestureRecognizerDirectionUp) {
        <#statements#>
    }
}
//对于长按手势UILongPressGestureRecognizer
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc]initWithTarget:self action:@selector(longPress:)];

// 默认触发两次,通常需要做一个判断
- (void)longPress:(UILongPressGestureRecognizer *)longPress
{   //长按开始时
    if(longPress.state == UIGestureRecognizerStateBegan){
        NSLog(@"%s",__func__);

    }
}
//关于旋转/拖拽/捏合手势
//在操作完之后要进行复位
如:rotation/pan/pinch  为三种手势对象
 // 旋转复位(因为角度不复位会叠加/不是相对于上一次,相对于一开始)
    rotation.rotation = 0;
    // 捏合复位
    pinch.scale = 1;
    // 拖拽复位
    [pan setTranslation:CGPointZero inView:self.imageView];
UIGestureRecognizerDelegate
// 是否允许接收触摸
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
        return YES/NO; // 允许/不允许
}
// 当用户需要同时使用多个手势的时候就会调用
// 判断是否支持多个手势,YES,支持(默认为No)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    return YES;
}

storyboard小发现

将Main.storyboard里的控件 连线到自定义view属性中
这里写图片描述
一般情况下 只能连线到控制器中
这边应该在自定义View中 先写好IBOutlet属性 进行反向连线
这里写图片描述

哈哈~这算不算黑魔法 算不算 算不算 算不算….

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值