文档章节

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

zh_iOS
 zh_iOS
发布于 2017/02/07 18:22
字数 980
阅读 561
收藏 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
博文 73
码字总数 33753
作品 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

没有更多内容

加载失败,请刷新页面

加载更多

下一页

咕泡-Factory设计模式笔记

个人感悟: 设计模式都是处理复杂问题的,如果问题本身很简单,使用设计模式反而累赘,增加了开发的复杂性 遇到最简单的情况,直接 new 如果创建对象的过程简单,但是需要匹配不同情况,返回...

职业搬砖20年
19分钟前
0
0
Java中的锁分类

在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏...

Funcy1122
27分钟前
0
0
Ansible随机数

想为你的Ansible剧本取一个随机数?还想在接下来的运行中保持系统的等幂性?这里有一个答案。 假如,你要为一大批服务器设置cron任务,却不想让它们同时启动,你可以这样设置分钟数: minute...

大别阿郎
37分钟前
0
0
SpringCloud之服务注册中心Eureka

本系列介绍的配置均基于 Spring Boot 2.0.1.RELEASE 版本和 Spring Cloud Finchley.SR1 服务注册中心 Spring Cloud 已经帮我们实现了服务注册中心,我们只需要很简单的几个步骤就可以完成。 ...

熊小飞呀
今天
9
1
“Comparison method violates ...”异常的再现方法

前提条件:JDK8 代码: import java.util.ArrayList;import java.util.Collections;import java.util.Comparator;import java.util.List;public class Test { public stat......

hunterli
今天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部