文档章节

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

zh_iOS
 zh_iOS
发布于 2017/02/07 18:22
字数 980
阅读 790
收藏 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
粉丝 27
博文 78
码字总数 35924
作品 0
石景山
程序员
私信 提问
IOS代码实现常用控件UIButton、UISlider、UISwitch、UISegmentedControl

IOS中最常用到的控件UIButton、UISlider、UISwitch、UISegmentedControl通过Xib文件拖动生成非常简单,其实用代码实现也是一样的简单,当然,用代码实现能够掌握到更多的东西。 上图中包涵提...

长平狐
2012/10/25
606
0
​Xamarin iOS教程之使用按钮接接收用户输入

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

大学霸
2015/06/16
0
0
实现 iOS 上的井字游戏

下载 demo - 70.1 KB 简介 本文介绍如何用 MVC 模式在 iOS 上实现双人对战的井字游戏. 读者最好有一些 iOS 编程基础, 以便更好的理解本文的代码. 希望这篇文章对提高读者的 iOS 和 MVC 编程水...

oschina
2014/11/09
2.5K
4
[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

没有更多内容

加载失败,请刷新页面

加载更多

记录"正则表达式"

详细请查看我的博客:https://blog.enjoytoshare.club/article/RegularExpression.html 1 写在前面 正则表达式(Regular Expression)在代码中常常简写为regex。正则表达式通常被用来检索、替...

wugenqiang
13分钟前
0
0
linux clamav 免费查毒工具

linux下需要杀毒工具吗?我一直认为是不要的,基于linux的病毒很少,linux 安全防护也做的很好,一般很难功破。我想那些黑客们更喜欢,写windows下的病毒,用的人多啊,传播也容易。下面的操...

老孟的Linux私房菜
16分钟前
0
0
关于连接PostgreSQL时提示 FATAL: password authentication failed for user "连接用户名" 的解决办法

关于连接PostgreSQL时提示 FATAL: password authentication failed for user "连接用户名" 的解决方法 今天帮一个同学解决了一个FATAL: password authentication failed for user "连接用户名......

dragon_tech
20分钟前
0
0
聊聊jest的IdleConnectionReaper

序 本文主要研究一下jest的IdleConnectionReaper IdleConnectionReaper jest-common-6.3.1-sources.jar!/io/searchbox/client/config/idle/IdleConnectionReaper.java public class IdleCon......

go4it
27分钟前
0
0
android 事件分发

记录下对Android事件分发的理解: 如果a作为viewgroup,b作为view,b包含在a中。事件会先传递到viewgroup中,既a。然后a会进行事件分发给子view。事件分发的方法中,会有个判断,是否要分发给...

醉雨
40分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部