iOS开发小记:运用Runtime机制扩大UIButton的响应区域

2024-06-09 01:32

本文主要是介绍iOS开发小记:运用Runtime机制扩大UIButton的响应区域,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

      本篇转载自李永乐的文章:原文链接 点击打开链接 

      在iOS开发中,有时会将一个UIButton的外观设置成很小,从而使其点按操作变得很不容易,因为点按的区域是根据UIButton的frame决定的。解决这个问题的办法一般是把button的frame放大,然后把里面的内容缩小,但是这样做总是感觉不伦不类,还需要分别重新调整Button的frame和显示内容的frame,比较麻烦。今天笔者在外国网站总结了一种方法,通过OC创建类别的方式,运用Runtime关联对象的方法,对UIButton进行了扩展 ,创建一个方法直接用于扩大Button的点按区域,而不改变它的显示内容,使代码的可读性大大增强。

       其执行原理为:OC中创建类别(Categroy)的方式,并不允许给已有的类扩展属性,只可以给其扩展方法。所以,需要使用Runtime“黑魔法”中的关联对象(Associative Object)的一些方法,动态地为某个button对象添加扩展距离的属性,然后检测UITouch事件的触摸点是否在我们扩展距离后Rect内,从而达到想要的效果。

       首先,创建一个UIButton的Category,起名为EnlargeTouchArea,设置一个外界可访问的方法setEnlaEdgeWithTop:right:bottom:left,在使用时也只需使用这个方法即可,传入的四个参数分别是上、右、下、左的扩展距离。

[objc]  view plain copy
  1. //  UIButton+EnlargeTouchArea.h  
  2.   
  3. #import <Foundation/Foundation.h>  
  4. #import <UIKit/UIKit.h>  
  5.   
  6. @interface UIButton (EnlargeTouchArea)  
  7.   
  8. - (void)setEnlargeEdgeWithTop:(CGFloat) top right:(CGFloat) right bottom:(CGFloat) bottom left:(CGFloat) left;  
  9.   
  10. @end  
 

        然后导入<objc/runtime.h>,所有Runtime的黑魔法都在这里

[objc]  view plain copy
  1. //  UIButton+EnlargeTouchArea.m  
  2.   
  3. #import "UIButton+EnlargeTouchArea.h"  
  4.   
  5. #import <objc/runtime.h>  
  6. @implementation UIButton (EnlargeTouchArea)  
  7.   
  8.   
  9. static char topNameKey;  
  10. static char rightNameKey;  
  11. static char bottomNameKey;  
  12. static char leftNameKey;  


        objc_setAssociatedObject是一个C语言函数,这个函数被称之为“关联API”,它的作用是把top、right、bottom、left这四个从外界获取到的值与本类(self)关联起来,然后设置一个static char作为能够找到他们的Key

[objc]  view plain copy
  1. - (void) setEnlargeEdgeWithTop:(CGFloat) top right:(CGFloat) right bottom:(CGFloat) bottom left:(CGFloat) left  
  2. {  
  3.     objc_setAssociatedObject(self, &topNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);  
  4.     objc_setAssociatedObject(self, &rightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);  
  5.     objc_setAssociatedObject(self, &bottomNameKey, [NSNumber numberWithFloat:bottom], OBJC_ASSOCIATION_COPY_NONATOMIC);  
  6.     objc_setAssociatedObject(self, &leftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);  
  7. }  


       objc_getAssociatedObject同样也是一个关联API(c语言函数),它可以通过刚刚设置的Key找到上个方法中从外界传入的top、right、bottom、left,这个api和obj_setAssociatedObject一起使用就可以达到给已有类扩展属性的效果。最后我们通过self.bounds设置一个新的CGRect,作为扩大后的点按区域,并且返回

[objc]  view plain copy
  1. - (CGRect) enlargedRect  
  2. {  
  3.     NSNumber* topEdge = objc_getAssociatedObject(self, &topNameKey);  
  4.     NSNumber* rightEdge = objc_getAssociatedObject(self, &rightNameKey);  
  5.     NSNumber* bottomEdge = objc_getAssociatedObject(self, &bottomNameKey);  
  6.     NSNumber* leftEdge = objc_getAssociatedObject(self, &leftNameKey);  
  7.     if (topEdge && rightEdge && bottomEdge && leftEdge)  
  8.     {  
  9.         return CGRectMake(self.bounds.origin.x - leftEdge.floatValue,  
  10.                           self.bounds.origin.y - topEdge.floatValue,  
  11.                           self.bounds.size.width + leftEdge.floatValue + rightEdge.floatValue,  
  12.                           self.bounds.size.height + topEdge.floatValue + bottomEdge.floatValue);  
  13.     }  
  14.     else  
  15.     {  
  16.         return self.bounds;  
  17.     }  
  18. }  

        

       这个方法UIView的一个实例方法,作用是,捕获当前的UITouch事件中的触摸点,检测它是否在最上层的子视图内,如果不是的话就递归检测其父视图。这样的话,我们就只是将当前某一个触摸的point与某一个rect进行比较,并没有改变Button真实的frame,从而真正的从逻辑上达到了只是扩大点按区域的效果。

[objc]  view plain copy
  1. - (UIView*) hitTest:(CGPoint) point withEvent:(UIEvent*) event  
  2. {  
  3.     CGRect rect = [self enlargedRect];  
  4.     if (CGRectEqualToRect(rect, self.bounds))  
  5.     {  
  6.         return [super hitTest:point withEvent:event];  
  7.     }  
  8.     return CGRectContainsPoint(rect, point) ? self : nil;  
  9. }  

        总结一下,为什么要用runtime去为已有类达到一个扩展属性的效果呢,正是因为上面这个方法,这个系统提供的方法并没有提供接受其他参数的地方,而我们却必须要指定一个扩大的区域作为参数,所以就必须为这个类扩展一个新的属性。

这篇关于iOS开发小记:运用Runtime机制扩大UIButton的响应区域的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1043877

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

python运用requests模拟浏览器发送请求过程

《python运用requests模拟浏览器发送请求过程》模拟浏览器请求可选用requests处理静态内容,selenium应对动态页面,playwright支持高级自动化,设置代理和超时参数,根据需... 目录使用requests库模拟浏览器请求使用selenium自动化浏览器操作使用playwright

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyQt5 GUI 开发的基础知识

《PyQt5GUI开发的基础知识》Qt是一个跨平台的C++图形用户界面开发框架,支持GUI和非GUI程序开发,本文介绍了使用PyQt5进行界面开发的基础知识,包括创建简单窗口、常用控件、窗口属性设... 目录简介第一个PyQt程序最常用的三个功能模块控件QPushButton(按钮)控件QLable(纯文本

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Spring Boot 中的默认异常处理机制及执行流程

《SpringBoot中的默认异常处理机制及执行流程》SpringBoot内置BasicErrorController,自动处理异常并生成HTML/JSON响应,支持自定义错误路径、配置及扩展,如... 目录Spring Boot 异常处理机制详解默认错误页面功能自动异常转换机制错误属性配置选项默认错误处理

Java中的xxl-job调度器线程池工作机制

《Java中的xxl-job调度器线程池工作机制》xxl-job通过快慢线程池分离短时与长时任务,动态降级超时任务至慢池,结合异步触发和资源隔离机制,提升高频调度的性能与稳定性,支撑高并发场景下的可靠... 目录⚙️ 一、调度器线程池的核心设计 二、线程池的工作流程 三、线程池配置参数与优化 四、总结:线程

基于Python开发一个图像水印批量添加工具

《基于Python开发一个图像水印批量添加工具》在当今数字化内容爆炸式增长的时代,图像版权保护已成为创作者和企业的核心需求,本方案将详细介绍一个基于PythonPIL库的工业级图像水印解决方案,有需要... 目录一、系统架构设计1.1 整体处理流程1.2 类结构设计(扩展版本)二、核心算法深入解析2.1 自

Zabbix在MySQL性能监控方面的运用及最佳实践记录

《Zabbix在MySQL性能监控方面的运用及最佳实践记录》Zabbix通过自定义脚本和内置模板监控MySQL核心指标(连接、查询、资源、复制),支持自动发现多实例及告警通知,结合可视化仪表盘,可有效... 目录一、核心监控指标及配置1. 关键监控指标示例2. 配置方法二、自动发现与多实例管理1. 实践步骤