iOS中的事件产生与传递

触摸事件

iOS中的事件可以分为3大类型:触摸事件,加速计事件,远程控制事件.在iOS中不是任何对象都能处理事件,只有继承了UIResponder的对象才能接收并处理事件。我们称之为“响应者对象”

UIView的触摸事件处理

UIView是UIResponder的子类,可以覆盖下列4个方法处理不同的触摸事件
  • 一根或者多根手指开始触摸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对象

UITouch

  • 当用户用一根手指触摸屏幕时,会创建一个与手指相关联的
    UITouch对象
  • 一根手指对应一个UITouch对象
  • UITouch的作用
    保存着跟手指相关的信息,比如触摸的位置、时间、阶段
  • 当手指移动时,系统会更新同一个UITouch对象,使之能够一直保存该手指在的触摸位置
  • 当手指离开屏幕时,系统会销毁相应的UITouch对象
  • 提示:iPhone开发中,要避免使用双击事件!
UITouch的属性
  • 触摸产生时所处的窗口
    @property(nonatomic,readonly,retain) UIWindow *window;

  • 触摸产生时所处的视图
    @property(nonatomic,readonly,retain) UIView *view;

  • 短时间内点按屏幕的次数,可以根据tapCount判断单击、双击或更多的点击
    @property(nonatomic,readonly) NSUInteger tapCount;

  • 记录了触摸事件产生或变化时的时间,单位是秒
    @property(nonatomic,readonly) NSTimeInterval timestamp;

  • 当前触摸事件所处的状态
    @property(nonatomic,readonly) UITouchPhase phase;

UITouch的方法
  • (CGPoint)locationInView:(UIView *)view;
    返回值表示触摸在view上的位置,这里返回的位置是针对view的坐标系的(以view的左上角为原点(0, 0)),调用时传入的view参数为nil的话,返回的是触摸点在UIWindow的位置

  • (CGPoint)previousLocationInView:(UIView *)view;
    该方法记录了前一个触摸点的位置

事件的产生和传递
  • 发生触摸事件后,系统会将该事件加入到一个由UIApplication管理的事件队列中

  • UIApplication会从事件队列中取出最前面的事件,并将事件分发下去以便处理,通常,先发送事件给应用程序的主窗口(keyWindow)

  • 主窗口会在视图层次结构中找到一个最合适的视图来处理触摸事件,这也是整个事件处理过程的第一步

  • 找到合适的视图控件后,就会调用视图控件的touches方法来作具体的事件处理

    • touchesBegan…
    • touchesMoved…
    • touchedEnded…
  • 如果父控件不能接收触摸事件,那么子控件就不可能接收到触摸事件(掌握)
  • 如何找到最合适的控件来处理事件?
    • 自己是否能接收触摸事件?
    • 触摸点是否在自己身上?
    • 从后往前遍历子控件,重复前面的两个步骤
    • 如果没有符合条件的子控件,那么就自己最适合处理
控件方法hitTest及pointInside

只要一旦点击控件就会调用
- hitTest:是控件方法
- hitTest作用:就是帮你寻找最合适的View
- hitTest什么调用:只要事件一传递给一个控件,就会调用这个这个控件的hitTest
- 点击白色 -> 产生触摸事件 -> UIApplication -> [UIWindow hitTest] -> [whiteView hitTest] return whiteView
- pointInside:就是用来判断点在不在方法调用者上

事件处理的本质和hitTest,pointInside使用。

// 事件 -> UIApplication -> UIWindow hitTest(寻找最合适view) -> WhiteView hitTest(遍历橘色,把事件传递给橘色,接着遍历绿色,把事件传递给绿色) ->


// 寻找最合适view
// point:也是方法调用者坐标系上的点
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{

    // 1.判断下自己能否接收触摸事件
    if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;

    // 2.判断下点在不在当前控件上
    // 用pointInside方法判断点在不在控件上,point这个必须是方法调用者坐标系上的点
    if ([self pointInside:point withEvent:event] == NO) return nil;

    // 3.从后往前遍历自己的子控件
    int count = (int)self.subviews.count;
    for (int i = count - 1; i >= 0; i--) {
        // 取出子控件
        UIView *childV = self.subviews[i];

        // 把自己坐标系上点转化成子控件坐标系上点
        CGPoint childP = [self convertPoint:point toView:childV];

        UIView *fitView = [childV hitTest:childP withEvent:event];

        // 如果找到最合适view就直接返回
        if (fitView) {
            return fitView;
        }

    }

    // 4.自己就是最合适view
    return self;

}

// 作用:判断point在不在方法调用者上
// point:必须是方法调用者坐标系上的点
// 什么时候调用:hitTest方法底层就会调用这个方法,判断点在不在控件
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    NSLog(@"%s",__func__);

    return NO;
}

事件传递demo

效果
  • 可以拖动“小黄人”按钮
  • 点击按钮后,出现一张图片,点击图片切换另一张图片
    demo
    注意点:在按钮上添加一个超过父控件大小的子控件,可以显示,但是点击没有效果。原因:事件 -> UIApplication -> UIWindow hitTest(寻找最合适view) -> 按钮 hitTest(不是点击在父控件按钮上,所以没有作用)
    在不做处理下,点击图片按钮会没有任何反应效果
代码
  • 自定义按钮,点击后添加子控件
// 小黄人
- (IBAction)btnClick:(PopButton *)sender {
    // 创建对话框按钮
    UIButton *chatView = [UIButton buttonWithType:UIButtonTypeCustom];
    [chatView setBackgroundImage:[UIImage imageNamed:@"小黄人1"] forState:UIControlStateNormal];
    [chatView setBackgroundImage:[UIImage imageNamed:@"小黄人2"] forState:UIControlStateHighlighted];

    [chatView sizeToFit];

    chatView.center = CGPointMake(chatView.bounds.size.width * 0.5, -chatView.bounds.size.height * 0.5);


    sender.chatView = chatView;

    [sender addSubview:chatView];
}
  • 自定义按钮中的实现
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    // 转化坐标系
    CGPoint chatP = [self convertPoint:point toView:_chatView];

    // 判断下点在不在chatView
    if ([_chatView pointInside:chatP withEvent:event]) {
        return _chatView;
    }else{
        return [super hitTest:point withEvent:event];
    }

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 获取UITouch对象
    UITouch *touch = [touches anyObject];
//    touch.v

    // 获取当前点
    CGPoint curP = [touch locationInView:self];

    // 获取上一个点
    CGPoint preP = [touch previousLocationInView:self];

    // 获取当前手指偏移量
    CGFloat offsetX = curP.x - preP.x;
    CGFloat offsetY = curP.y - preP.y;

    // 移动按钮
    self.transform = CGAffineTransformTranslate(self.transform, offsetX, offsetY);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值