文档章节

iOS扩大UIButton按钮的可点击区域

zh_iOS
 zh_iOS
发布于 2017/02/07 18:22
字数 980
阅读 617
收藏 0

一、开发中遇到的问题

我们在开发时有时遇到这中情况:UI给的图片很小,button的点击区域要求比较大。如果用 setBackgroundImage: 方式设置图片会导致图片也跟着button的frame放大,如果使用 setImage: 设置图片,图片虽然不会跟着button的frame 发生变化,但是想要调整好图片显示的位置确很麻烦。

二、分析解决方法

因此最好的办法就是:设置button的大小刚好就是图片的大小,此时再调整图片frame的时候就非常方便了 (button的frame就是图片的frame)。接下来要解决的问题就是扩大button的点击区域。

三、解决方案

1、在原来button的基础上再添加一个透明的大button(不推荐)

2、继承 UIButton 然后重写 pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    CGRect bounds = self.bounds;
    // 若原热区小于44x44,则放大热区,否则保持原大小不变
    CGFloat deltaW = MAX(44 - bounds.size.width, 0);
    CGFloat deltaH = MAX(44 - bounds.size.height, 0);
    bounds = CGRectInset(bounds, -deltaW * 0.5, -deltaH * 0.5);
    return CGRectContainsPoint(bounds, point);
}

使用:

    ZHButton *btn = [[ZHButton alloc] initWithFrame:CGRectMake(100, 100, 20, 20)];
    [btn addTarget:self action:@selector(log) forControlEvents:UIControlEventTouchUpInside];
    btn.backgroundColor = [UIColor orangeColor];
    [self.view addSubview:btn];

虽然button 的宽和高都只有 20 但是按钮能响应点击的热点区域却在原点击区域基础上上下左右各扩展了 (44 - 20)/ 2 = 21 .如此一来button 的可点击区域就非常大了,而且button 的frame也没有受到影响。

3、给button添加一个分类,使用对象关联设置button 的热点区域

其实其思路和方法二一致,不过使用起来更方便了。

.h 文件

@interface UIButton (EnlargeArea)

- (void)setEnlargeEdgeWithTop:(CGFloat)top right:(CGFloat)right bottom:(CGFloat)bottom left:(CGFloat)left;

@end

.m 文件

#import <objc/runtime.h>
@implementation UIButton (EnlargeArea)
static char topNameKey;
static char rightNameKey;
static char bottomNameKey;
static char leftNameKey;

- (void)setEnlargeEdgeWithTop:(CGFloat) top right:(CGFloat) right bottom:(CGFloat) bottom left:(CGFloat) left
{
    objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);
    objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (CGRect)enlargedRect
{
    NSNumber* topEdge = objc_getAssociatedObject(self, &topNameKey);
    NSNumber* rightEdge = objc_getAssociatedObject(self, &rightNameKey);
    NSNumber* bottomEdge = objc_getAssociatedObject(self, &bottomNameKey);
    NSNumber* leftEdge = objc_getAssociatedObject(self, &leftNameKey);
    if (topEdge && rightEdge && bottomEdge && leftEdge) {
        return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,
                          self.bounds.origin.y - topEdge.floatValue,
                          self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,
                          self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);
    } else {
        return self.bounds;
    }
}


- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    if (self.isHidden||!self.isUserInteractionEnabled) {
        return NO;
    }
    CGRect responseArea = [self enlargedRect];
    return CGRectContainsPoint(responseArea, point);
}
@end

4.使用objc运行时交换 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法的实现,在自己的实现逻辑中进行判断,其中 extendedHitArea属性 是你要扩展的可以点击的区域,在pointInside方法里判断点击的坐标是否在可响应点击区域,如果在返回YES,否则返回NO 。

// extendHitArea 是暴露出来的一个属性 。
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method originalM = class_getInstanceMethod([self class], @selector(pointInside:withEvent:));
        Method swzM = class_getInstanceMethod([self class], @selector(swz_pointInside:withEvent:));
        BOOL addMethod = class_addMethod([self class], @selector(pointInside:withEvent:), method_getImplementation(swzM), method_getTypeEncoding(swzM));
        if (addMethod) {
            class_replaceMethod([self class], @selector(swz_pointInside:withEvent:), method_getImplementation(originalM), method_getTypeEncoding(originalM));
        } else {
            method_exchangeImplementations(originalM, swzM);
        }
    });
}

- (void)setExtendedHitArea:(CGRect)extendedHitArea {
    objc_setAssociatedObject(self, @selector(extendedHitArea), @(extendedHitArea), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (CGRect)extendedHitArea {
    CGRect rect = [objc_getAssociatedObject(self, @selector(extendedHitArea)) CGRectValue];
    if (CGRectEqualToRect(rect, CGRectZero)) {
        return self.bounds;
    }
    return rect;
}

/*!
 @method 可以响应点击事件的rect
 */
- (CGRect)responseHitArea {
    CGRect extendedHitArea = [objc_getAssociatedObject(self, @selector(extendedHitArea)) CGRectValue];
    if (!CGRectEqualToRect(extendedHitArea, CGRectZero)) {
        return                       CGRectMake(
                                     self.bounds.origin.x-extendedHitArea.origin.x,
                                     self.bounds.origin.y-extendedHitArea.origin.y,
                                     self.bounds.size.width+extendedHitArea.origin.x+extendedHitArea.size.width,
                                     self.bounds.size.height+extendedHitArea.origin.y+extendedHitArea.size.height);

    } else {
        return self.bounds;
    }
}

- (BOOL)swz_pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    BOOL result = [self swz_pointInside:point withEvent:event];
    CGRect responseHitArea = [self responseHitArea];
    BOOL ret = ( result
                ||CGRectEqualToRect(responseHitArea, CGRectZero)
                ||!self.isUserInteractionEnabled
                ||self.isHidden);
    if (ret) return result;
    return CGRectContainsPoint(responseHitArea, point);
}

以上就是扩大按钮点击区域的4中常见方法 ,第3中和第四种,不仅仅适用于按钮,也同样适用于其他view组件 。例如:在view上添加一个手势 ,第三四中方法可以直接写成 一个view的分类,拖到项目直接使用简单方便 。

© 著作权归作者所有

共有 人打赏支持
zh_iOS
粉丝 26
博文 74
码字总数 34182
作品 0
石景山
程序员
[iOS Animation]-CALayer 专用图层 富文本

富文本 iOS 6中,Apple给UILabel和其他UIKit文本视图添加了直接的属性化字符串的支持,应该说这是一个很方便的特性。不过事实上从iOS3.2开始CATextLayer就已经支持属性化字符串了。这样的话,...

浩浩老师
2015/09/23
0
0
《小印记》iOS源码分享--自定义弹框篇

笔者前不久终于发布了自己的APP《小印记》,在此分享一些iOS源码,如果读者学到了有用的东西,希望能前往App Store下载《小印记》支持一下笔者,谢谢! 《小印记》iOS源码分享--极光推送实践...

_子墨
2017/03/22
0
0
​Xamarin iOS教程之使用按钮接接收用户输入

Xamarin iOS教程之使用按钮接接收用户输入 Xamarin iOS使用按钮接接收用户输入 按钮是用户交互的最基础控件。即使是在iPhone或者iPad中,用户使用最多操作也是通过触摸实现点击。而点击操作最...

大学霸
2015/06/16
0
0
用视图上移解决UITextField/UITextView被键盘遮盖问题

先看看UILabel/UITextField/UITextView的区别: UILabel 显示的文本只读,无法编辑,可以根据文字个数自动换行; UITextField 可编辑本文,但是无法换行,只能在一行显示;当点击键盘上的ret...

yoyoso
2015/03/18
0
0
​Xamarin iOS教程之自定义视图

Xamarin iOS教程之自定义视图 Xamarin iOS自定义视图 工具栏中的视图在实际应用开发中用的很多,但是为了吸引用户的眼球,开发者可以做出一些自定义的视图。 【示例2-33】以下将实现一个自定...

大学霸
2015/06/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Thinkphp5 优雅配置两个数据库

工作需要需要配置两个数据库,框架5.0的,步骤如下: 1、在database.php同级创建一个database2.php文件 在里面配置第二个数据库信息, 2、在config中配置这个数据库信息: 3、创建第二个表的...

wqzbxh
5分钟前
0
0
Socket网络编程进阶与实战

Socket网络编程进阶与实战 Socket对于每个工程师的重要性不言而喻。本课程将理论结合实践,带你从零开始,系统学习Socket编程技术,让Socket的学习不再那么零散与难以掌握,同时会提炼出Soc...

qq__2304636824
11分钟前
0
0
Android studio常用快捷键

Ctrl +Alt +Space //显示可用参数 Ctrl + Alt +M //抽取方法 Ctrl +Alt + F //提取全局变量 Ctrl +Shift + "+或-" //折叠/展开代码块 Shift + F6 //批量更改变量 Ctrl + Tab //切换器 Ctrl +...

lanyu96
23分钟前
0
0
@ControllerAdvice 拦截异常并统一处理

在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。 一、介绍 创建 MyControllerAdvice,并添...

狼王黄师傅
27分钟前
0
0
ajax传递参数给springmvc总结[转]

https://www.cnblogs.com/franson-2016/p/6770028.html https://www.cnblogs.com/xiaoxi/p/5708084.html 总结: 1.springmvc与Ajax交互,可以传入三种类型的数据: (1)文本:"uname=alice&......

废柴
29分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部