美文网首页
UIButton实现区域外点击

UIButton实现区域外点击

作者: spicyShrimp | 来源:发表于2017-09-20 15:32 被阅读34次

UIButton实现区域外点击

今天项目开发中偶然需要这个功能,一个按钮区域大小外也要能够点击响应

因为项目是很早就开发的就项目,所以没有必要因为这个小的需求来修改按钮的大小,重新制定约束,或者frame等等,因为可能会影响到其他控件的约束或者响应等,
那么如何能够实现这个功能呢?

其实最简单的方法就是创建一个那么大的按钮,然后按钮的图片设置小一点的,让人看上去按钮就那么大,但是其实要比我们看到的要大.
这样的实现相信每一个人都会,至于我为什么没有用,上面也提到了

于是第一时间想到了touchBegin,我们可以获取点击的坐标

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    CGPoint location = [[touches allObjects][0] locationInView:self.view];
    //...
}

然后根据坐标判断是否响应某一个方法,来达到目的
这样确实能够实现,但是如果一个界面有很多按钮都需要这样判断,那么是不是就很麻烦了呢?
有没有什么比较好的解决方案呢?
一个按钮的点击范围,那么最好的方案肯定是在这个按钮内做判断.
选择自定义一个按钮?
外层是按钮,中间是一个小的图片?那么和开始提到的一样了

所以我应该试着在touch,或者简单的在point上面做点文章
UIButton很简单,从头阅读到尾,也没有看到可用的信息.
那么进入其父类UIControl,很显然,也很失望,这个类里面都是响应的属性或者方法,能够判断点击的执行或者取消等等,但是诸如范围大小的字眼,很难看到,
于是继续深入,找到UIView
终于,没有过几行,我就发现了一个好东西

- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;   // default returns YES if point is in bounds

这个是什么方法呢?
这个函数的用处是判断当前的点击或者触摸事件的点是否在当前的view中。默认是当点在视图大小的范围内返回yes,之外则返回no

这正是我们想要的不是么?
我们可以重写这个方法.在范围外的某一片区域内,我们都返回YES,
那么其判定就是当前的点击或者触摸时间的点就在当前的view中
我们可以选择建立一个特殊的Button,也可以选择创建UIButton的分类,
很显然,这里我创建分类的话,可以做到不修改其他项目代码,就能很轻松的实现需求

于是创建分类
很容易的就能想到,我们可能需要创建一个属性,叫做点击区域范围

#import <UIKit/UIKit.h>

@interface UIButton (SXTouchAreaInsets)

/**
 设置按钮额外点击范围
 */
@property (nonatomic, assign) UIEdgeInsets sx_touchAreaInsets;

@end
#import "UIButton+SXTouchAreaInsets.h"
#import <objc/runtime.h>

@implementation UIButton (SXTouchAreaInsets)

- (UIEdgeInsets)sx_touchAreaInsets
{
    return [objc_getAssociatedObject(self, @selector(sx_touchAreaInsets)) UIEdgeInsetsValue];
}

- (void)setSx_touchAreaInsets:(UIEdgeInsets)touchAreaInsets
{
    NSValue *value = [NSValue valueWithUIEdgeInsets:touchAreaInsets];
    objc_setAssociatedObject(self, @selector(sx_touchAreaInsets), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    UIEdgeInsets touchAreaInsets = self.sx_touchAreaInsets;
    CGRect bounds = self.bounds;
    bounds = CGRectMake(bounds.origin.x - touchAreaInsets.left,
                        bounds.origin.y - touchAreaInsets.top,
                        bounds.size.width + touchAreaInsets.left + touchAreaInsets.right,
                        bounds.size.height + touchAreaInsets.top + touchAreaInsets.bottom);
    return CGRectContainsPoint(bounds, point);
}

@end

于是set方法和get方法不用说明

然后重写pointInside:withEvent:即可
通过计算自身的大小和设置的区域的大小来判断点击的点是否在该范围内,而决定是否响应点击事件.
很轻松的搞定了.

至此,我只需要在想要有点击额外区域的UIButton中添加一行

btn.sx_touchAreaInsets = UIEdgeInsetsMake(20, 20, 20, 20);

区域外点击事件就这样轻松的实现了.
对原来的项目不需要很大的改动,不需要修改按钮的大小和位置,不需要调节其他控件的约束,不需要设置新的图片,只要在需要区域点击的按钮添加一个属性值的设置即可

当然,我们也可以选择创建UIView的分类,这样所有的视图都会有这个属性,但是个人觉得没有太大的用处,因为这样的需求本来就不多,再加上不是按钮的可能性,个人觉得就更低了.

一个简单的案例吧,今天碰到的,然后分享出来
demo就不贴出来了.主要说的其实就是死思路和- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event这个方法

其实说到底就是一个事件传递的知识
iOS 事件传递 hitTest方法与PointInside方法
作用:寻找最适合的View

参数:当前手指所在的点.产生的事件

返回值:返回谁,谁就是最适合的View.

什么时候用调用:只要一个事件,传递给一个控件时,就会调用这个控件的hitTest方法


-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

作用:判断point在不在方法调用者上

point:必须是方法调用者的坐标系

什么时候调用:hitTest方法底层会调用这个方法,判断点在不在控件上.


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

return YES;

}

hitTest底层实现:

1.判断当前能不能接收事件


if(self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01)

returnnil;

2.判断触摸点在不在当前的控件上

if(![self pointInside:point withEvent:event]) 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];
    //判断自己的子控件是不是最适合的View
    UIView *fitView =[childV hitTest:childP withEvent:event];
    //如果子控件是最适拿的View,直接返回
    if (fitView) {
        returnfitView;
    }
}

4.自己就是最适合的View

return self.

相关文章

网友评论

      本文标题:UIButton实现区域外点击

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