欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

举例讲解iOS应用开发中hitTest触摸事件的编写方法

程序员文章站 2023-11-26 16:53:52
 hittest:withevet  调用过程 比如如果是当前的view a, 还有一个viewb 如果不重写 hittest 方法,那么 系统默认...

 hittest:withevet  调用过程

比如如果是当前的view a, 还有一个viewb

如果不重写 hittest 方法,那么 系统默认是先调用viewa的hitest 方法,然后再调用viewb的htest方法。

系统的调用过程,跟下面的重写hitest的方法是一模一样的。

复制代码 代码如下:

-(uiview*)hittest:(cgpoint)point withevent:(uievent *)event 

    if ([self pointinside:point withevent:event]) { 
    } 
    else { 
        return nil; 
    } 
    for (uiview *subview in self.subviews) { 
        if ([subview hittest:point withevent:event]!=nil) { 
            return subview; 
        } 
    } 
     
    return self; 


在说明一次,如果不重写hitest方法,那么每一个uivieew的默认hitest的方法都是上面这个处理流程。

那也没啥好说的。

但是对于一些特殊的处理过程,就不行了

所以之所以重写hittest方法,通常都是为了穿透上层 的 uiview,让touch事件可以达到下面的uiview,

比如 view a  和 view b,

view b完全挡住了view a,但是我想让点击viewb的时候,view a可以响应点击的事件。就可以采用下面的方法:

复制代码 代码如下:

-(uiview*)hittest:(cgpoint)point withevent:(uievent *)event 

    if ([self pointinside:point withevent:event]) { 
        nslog(@"in view a"); 
        return self; 
    } 
    else { 
        return nil; 
    } 
 

深入
我们来更深入一下,现在有个实例需求界面如下,

window

  -viewa

    -buttona

    -viewb

      -buttonb

层次结构:viewb完全盖住了buttona,buttonb在viewb上,现在需要实现:
(1)buttona和buttonb都能响应消息 (2)viewa也能收到viewb所收到的touches消息 (3)不让viewb(buttonb)收到消息。

(首先解析下,默认情况下,点击了buttonb的区域,ios消息处理过程。

-viewa

  -buttona

  -viewb

    -buttonb

当点击buttonb区域后,处理过程:从viewa开始依次调用hittest

pointinside的值依次为:

viewa:no;

viewb:yes;

buttonb:yes;

buttonb的subviews:no;

所以buttonb的subviews的hittest都返回nil,于是返回的处理对象是buttonb自己。接下去开始处理touches系列方法,这里是调用buttonb绑定的方法。处理完后消息就停止,整个过程结束。)

分析:

实现的方式多种,这里将两个需求拆解开来实现,因为实现2就可以满足1。

需求1的实现,viewb盖住了buttona,所以默认情况下buttona收不到消息,但是在消息机制里寻找消息响应是从父view开始,所以我们可以在viewa的hittest方法里做判断,若touch point是在buttona上,则将buttona作为消息处理对象返回。

代码如下:

复制代码 代码如下:

#pragma mark - hittest
- (uiview *)hittest:(cgpoint)point withevent:(uievent *)event
{
    // 当touch point是在_btn上,则hittest返回_btn
    cgpoint btnpointina = [_btn convertpoint:point fromview:self];
    if ([_btn pointinside:btnpointina withevent:event]) {
        return _btn;
    }
    
    // 否则,返回默认处理
    return [super hittest:point withevent:event];
    
}

这样,当触碰点是在buttona上时,则touch消息就被拦截在viewa上,viewb就收不到了。然后buttona就收到touch消息,会触发onclick方法。

需求2的实现,上面说到响应链,viewb只要override掉touches系列的方法,然后在自己处理完后,将消息传递给下一个响应者(即父view即viewa)。

代码如下:在viewb代码里

复制代码 代码如下:

#pragma mark - touches
- (void)touchesbegan:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"b - touchesbeagan..");
    
    // 把事件传递下去给父view或包含他的viewcontroller
    [self.nextresponder touchesbegan:touches withevent:event];
}
 
- (void)touchescancelled:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"b - touchescancelled..");
    // 把事件传递下去给父view或包含他的viewcontroller
    [self.nextresponder touchesbegan:touches withevent:event];
}
 
- (void)touchesended:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"b - touchesended..");
    // 把事件传递下去给父view或包含他的viewcontroller
    [self.nextresponder touchesbegan:touches withevent:event];
}
 
- (void)touchesmoved:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"b - touchesmoved..");
    // 把事件传递下去给父view或包含他的viewcontroller
    [self.nextresponder touchesbegan:touches withevent:event];
    
}

然后,在viewa上就可以接收到touches消息,在viewa上写:
复制代码 代码如下:

#pragma mark - touches
- (void)touchesbegan:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"a - touchesbeagan..");
}
 
- (void)touchescancelled:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"a - touchescancelled..");
}
 
- (void)touchesended:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"a - touchesended..");
}
 
- (void)touchesmoved:(nsset *)touches withevent:(uievent *)event
{
    nslog(@"a - touchesmoved..");
    
}

这样就实现了向父view透传消息。

不让viewb收到消息,可以设置viewb.userinteractionenable=no;除了这样还可以override掉viewb的ponitinside,原理参考上面。

在viewb上写:

复制代码 代码如下:

- (bool)pointinside:(cgpoint)point withevent:(uievent *)event
{
    // 本view不响应用户事件
    return no;
 
}