引言
关于笔者对Runtime的理解,将在下一篇博文中与大家分享。
该篇博文,笔者将向大家分享一个Runtime实用的技巧,在实际开发中也会经常用到。
说明
假设你现在需要实用一个系统的类,UIButton,UILabel等,但是你觉得系统的类无法满足你的需求,你将会怎么做?
一般我们都会想到继承,确实这个可以实现。但是只是为了增加一个属性,而去继承一个类,未免太麻烦,要是类多了,管理维护也是很头疼的一件事。这个时候,runtime的关联对象就发挥了作用。
今天在这篇博文中,我给系统UIButton扩大点击范围,希望能给大家带来此方法其他作用实现的一个思路。
实战
1.给UIButton添加一个分类。
(分类是什么在此就不赘述,对类别的深入理解请移步category详解)
#import <UIKit/UIKit.h>
@interface UIButton (IncreaseTapArea)
- (void)addIncreaseTapAreaWithTop:(CGFloat)top buttom:(CGFloat)buttom left:(CGFloat)left right:(CGFloat)right;
@end
实现文件如下:
1.导入runtime头文件
#import "UIButton+IncreaseTapArea.h"
#import <objc/runtime.h>
2.定义全局变量,用其地址作为关联对象的key
static char kTopNameKey;
static char kRightNameKey;
static char kButtomNameKey;
static char kLeftNameKey;
3.设置关联对象
- (void)addIncreaseTapAreaWithTop:(CGFloat)top buttom:(CGFloat)buttom left:(CGFloat)left right:(CGFloat)right {
objc_setAssociatedObject(self, &kTopNameKey, [NSNumber numberWithFloat:top], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &kRightNameKey, [NSNumber numberWithFloat:right], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &kButtomNameKey, [NSNumber numberWithFloat:buttom], OBJC_ASSOCIATION_COPY_NONATOMIC);
objc_setAssociatedObject(self, &kLeftNameKey, [NSNumber numberWithFloat:left], OBJC_ASSOCIATION_COPY_NONATOMIC);
}
objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)的四个参数:
1).需要关联对象的对象,在这里是UIButton类的对象,即self。
2).关联对象的key的地址,个人理解类似于定义属性时的属性名,在获取时会根据这个唯一地址取值。
3).id value 即关联对象,类似于属性值。
4).关联策略,有以下几种策略:
/**
* Policies related to associative references.
* These are options to objc_setAssociatedObject()
*/
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0,
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
OBJC_ASSOCIATION_RETAIN = 01401,
OBJC_ASSOCIATION_COPY = 01403
};
这些跟@property语法中的选项一个意思
4.在该API中返回扩大点击范围后的rect
- (CGRect)IncreaseAreaRect
{
NSNumber *top = objc_getAssociatedObject(self, &kTopNameKey);
NSNumber *right = objc_getAssociatedObject(self, &kRightNameKey);
NSNumber *buttom = objc_getAssociatedObject(self, &kButtomNameKey);
NSNumber *left = objc_getAssociatedObject(self, &kLeftNameKey);
if (top && right && buttom && left)
{
return CGRectMake(self.bounds.origin.x - left.floatValue,
self.bounds.origin.y - top.floatValue,
self.bounds.size.width + left.floatValue + right.floatValue,
self.bounds.size.height + top.floatValue + buttom.floatValue);
}
else
{
return self.bounds;
}
}
objc_getAssociatedObject(<#id object#>, <#const void *key#>)
该方法是通过先前定义的唯一key地址获取到该地址对应的value。
5.根据扩大后的rect,在hitTest方法中返回该rect(对hitTest方法不理解的请移步hitTest详解)
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent*)event
{
CGRect rect = [self IncreaseAreaRect];
if (CGRectEqualToRect(rect, self.bounds))
{
return [super hitTest:point withEvent:event];
}
return CGRectContainsPoint(rect, point) ? self : nil;
}
到这里我们给按钮扩大点击范围就已经完成了,接下来,只要在需要的地方导入该头文件,button对象调用该方法,传入你希望扩大的范围值就可以了。
其实,这只是runtime动态关联对象很简单的一个例子,相信大家从这个例子可以发散出很多技巧,更快捷,有效的完成开发。