美文网首页iOS资料汇总iOS - 音视频/图文/动画iOS转发
如果一个button有一部分超出父控件的范围了,这部分无法响应点

如果一个button有一部分超出父控件的范围了,这部分无法响应点

作者: 2a0d699cb83d | 来源:发表于2016-04-08 14:32 被阅读4557次

标题中的需求其实常常能遇到,如下图

图 1

当按钮超出Tab bar的view后,那么其实按钮超出的部分是无法被点击的。那么先来说说解决办法

1.我们重写蓝色view的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event的方法

  - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    //if内的条件应该为,当触摸点point超出蓝色部分,但在黄色部分时
    if (.....){
     return YES;
    }
    return NO;
  }

那么以上为什么能解决方法?

这和iOS的事件分发机制 hit-Testing有关,简单的说,hit-Testing的作用就是找出你每次触摸屏幕,点到的究竟是哪个view。

比如以下这个图


图 2

当我去点击View-C的时候,hit-Testing实际上是这样检测的
1.首先,视图会先从View-A开始检查,发现触摸点在View-A,所以检查View-A的子视图View-B。
2.发现触摸点在View-B内,好棒!看看View-B内的子视图View-C。
3.发现触摸点在View-C内,但View-C没有子视图了,所以View-C是此次触摸事件的hit-TestView了。

那么UIView中其实提供了两个方法来确定hit-TestView
1.- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
2.- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;//这个就是我们上面重写的方法

注意其实在每次递归去调用hitTest:(CGPoint)point withEvent:(UIEvent *)event之前,都会调用pointInside:withEvent:来确定该触摸点是否在该View内。

所以当我们重写pointInside:(CGPoint)point withEvent:(UIEvent *)event后,其实我们的点击后调用hitTest来递归的找hit-TestView的区域从这样:
图 3
变成了这样:
图 4

这样当我们愉快的点击上半凸起的区域时,hit-Testing便回去检查蓝色视图内的子视图,即黄色区域。从而来完成此次触摸事件。
Enjoy :)

2017年3月1日更新

针对gwk_ios提问,我首先表示感谢,我们有必要把这个问题拿出来,研究一下。
让我们先看个图:

图 5

恩,这图似乎有点大。不过没事。
gwk_iost提出了2个问题

如果- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法只要return yes 就能点击超出父view边界的子view上的button呢

这样做实际上会有一个问题,如上图,如果在View A中,先添加View B(包含了Button A),再添加Button B,那么再点击Button B时,Button B的方法会被正确触发。
但是,[如果在View A中,先添加Button B,再添加View B(包含了Button A)],这个时候,你会发现,点击Button B时,方法无法被正确触发了。因为你在点击View A内的任意位置时,系统会优先调用View B 的pointInSide: WithEvent:方法,这时候你返回YES,就截断了事件,包括点击Button B。

那么 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 内if判断该怎么写呢

其实point会返回的坐标是基于该View的坐标系(所以超出该View时,可能会出现负数),以iphone 7的屏幕大小为例子:

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

    if (CGRectContainsPoint(CGRectMake(0, 0, 87.5, 100),point)||CGRectContainsPoint(CGRectMake(87.5, -100, 200, 200), point)||CGRectContainsPoint(CGRectMake(287.5, 0, 87.5, 100),point)) {
        return YES;
    }
    return NO;
}

Enjoy :)

相关文章

网友评论

  • 陆宝宝:感谢楼主详细的分享,可是最后if判断里面的坐标系原点在哪里呢,能具体画出坐标系么
  • 孤胆走天涯://btn 换成自己的btn控件,不用算距离
    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *view = [super hitTest:point withEvent:event];
    if (view == nil) {
    CGPoint tempoint = [btn convertPoint:point fromView:self];
    if (CGRectContainsPoint(btn.bounds, tempoint))
    {
    view = btn;
    }
    }
    return view;
    }
  • 韦恩时代:也可以复写- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;方法转换subview坐标系来解决。
  • Maxdon:有些图怎么看不了的?
  • nasy_iOS:如果能拿到蓝色的tabar和黄色的button,if条件内直接写 CGRectContainsPoint(tabbar.frame,point)||CGRectContainsPoint(button.frame, point) 会出问题吗?
    凤栖林:tabbar.bounds就没问题
  • 毛小虎:你就你能给个demo???
  • gwk_iOS:感谢楼主.
    为什么我发现
    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
    方法只要return yes 就能点击超出父view边界的子view上的button呢...
    我并没有做出什么判断...
    2a0d699cb83d:@大转变 已更新文章
    a2df7b147ad6:为什么- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event这个方法不走 if判断条件怎么写 谢谢
    2a0d699cb83d:这是一定的噢,因为直接return yes 而不做限制时,就相当于你无论点击哪里,系统都会认为该触摸点在该View内
  • Pusswzy:受教了,终于清楚了响应者时间是从父视图开始检查,那么包括UIApplication->UIWindow么
    2a0d699cb83d:@Pusswzy 可以打印下查找hitTestView过程中的view,是从UIWindow开始的
  • 喵小帅:问一下方法里面的判断条件具体应该怎么写。。。我写了还是不响应
    2a0d699cb83d:@喵小帅 看具体的需求,比如上述例子中,条件之一可以是point.y<0&&point.y>-100
  • ccc7cdab373b:大神 怎么用按钮循环呢 和发消息一样
    2a0d699cb83d:@jerryIOS 抱歉~不是太明白你的意思

本文标题:如果一个button有一部分超出父控件的范围了,这部分无法响应点

本文链接:https://www.haomeiwen.com/subject/lasvlttx.html