RunTime在开发中的主要应用场景:
- 给分类增加属性
- 交换方法
一、给分类增加属性
1.控制button的响应区域
extension UIButton {
/* 更改按钮的点击区域 */
private struct RunTimeButtonKey {
static let topEdgeKey = UnsafeRawPointer.init(bitPattern: "topEdgeKey".hashValue)
static let rightEdgeKey = UnsafeRawPointer.init(bitPattern: "rightEdgeKey".hashValue)
static let bottomEdgeKey = UnsafeRawPointer.init(bitPattern: "bottomEdgeKey".hashValue)
static let leftEdgeKey = UnsafeRawPointer.init(bitPattern: "leftEdgeKey".hashValue)
}
private var topEdge: CGFloat? {
set {
objc_setAssociatedObject(self, RunTimeButtonKey.topEdgeKey!, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
return objc_getAssociatedObject(self, RunTimeButtonKey.topEdgeKey!) as? CGFloat
}
}
private var rightEdge: CGFloat? {
set {
objc_setAssociatedObject(self, RunTimeButtonKey.rightEdgeKey!, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
return objc_getAssociatedObject(self, RunTimeButtonKey.rightEdgeKey!) as? CGFloat
}
}
private var bottomEdge: CGFloat? {
set {
objc_setAssociatedObject(self, RunTimeButtonKey.bottomEdgeKey!, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
return objc_getAssociatedObject(self, RunTimeButtonKey.bottomEdgeKey!) as? CGFloat
}
}
private var leftEdge: CGFloat? {
set {
objc_setAssociatedObject(self, RunTimeButtonKey.leftEdgeKey!, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
return objc_getAssociatedObject(self, RunTimeButtonKey.leftEdgeKey!) as? CGFloat
}
}
func setEdge(top: CGFloat?, right: CGFloat?, bottom: CGFloat?, left: CGFloat?) {
self.topEdge = top
self.rightEdge = right
self.bottomEdge = bottom
self.leftEdge = left
}
open override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let top = topEdge ?? 0
let right = rightEdge ?? 0
let bottom = bottomEdge ?? 0
let left = leftEdge ?? 0
let rect = CGRect(x: bounds.origin.x - left, y: bounds.origin.y - top, width: bounds.size.width + left + right, height: bounds.size.height + top + bottom)
return rect.contains(point) ? self : nil
}
}
使用:
let button = UIButton(frame: CGRect(x: 0, y: 150, width: 200, height: 100))
button.center.x = view.center.x
button.backgroundColor = UIColor.red
button.addTarget(self, action: #selector(buttonClick), for: .touchUpInside)
/* 将按钮的响应区域按照四个方向分别增加50 */
button.setEdge(top: 50, right: 50, bottom: 50, left: 50)
view.addSubview(button)
2.控制点击手势的响应时间间隔
extension UITapGestureRecognizer: UIGestureRecognizerDelegate {
/* 控制手势点击的响应时间间隔 */
private struct RunTimeTapKey {
static let tapGestureDurationKey = UnsafeRawPointer.init(bitPattern: "tapGestureDuration".hashValue)
}
private var tapDuration: TimeInterval? {
set {
objc_setAssociatedObject(self, RunTimeTapKey.tapGestureDurationKey!, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
get {
return objc_getAssociatedObject(self, RunTimeTapKey.tapGestureDurationKey!) as? TimeInterval
}
}
convenience init(target: Any?, action: Selector?, duration: TimeInterval?) {
self.init(target: target, action: action)
tapDuration = duration
delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
isEnabled = false
let time = tapDuration ?? 0.0
DispatchQueue.main.asyncAfter(deadline: .now() + time) {
self.isEnabled = true
}
return true
}
}
使用:
/* 初始化时采用新增的初始化方式,设置响应间隔 */
let tap = UITapGestureRecognizer(target: self, action: #selector(tapClick), duration: 5.0)
tapView.addGestureRecognizer(tap)
二、交换方法
交换view的setBackgroundColor:方法:
@implementation UIView (MyBackgroundColorView)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// objc_getClass([self class]); 替换类方法
// [self class]; 替换实例方法
// 系统原本的设置背景颜色的方法
Method originalMethod = class_getInstanceMethod(class, @selector(setBackgroundColor:));
// 自定义的设置背景颜色的方法
Method myMethod = class_getInstanceMethod(class, @selector(yyp_setBackgroundColor:));
// 给原方法添加实现,预防原方法没有实现
BOOL addMethod = class_addMethod(class, @selector(setBackgroundColor:), method_getImplementation(myMethod), method_getTypeEncoding(myMethod));
if (addMethod) {
// 将原方法实现替换到交换方法的实现
class_replaceMethod(class, @selector(yyp_setBackgroundColor:), method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
// 原方法已经实现,直接交换两个方法
method_exchangeImplementations(originalMethod, myMethod);
}
});
}
- (void)yyp_setBackgroundColor:(UIColor *)color
{
NSLog(@"yyp_setBackgroundColor");
// 这里不会造成死循环,因为yyp_setBackgroundColor已经与setBackgroundColor交换了实现方法
[self yyp_setBackgroundColor:color];
}
@end
使用:
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
view.center = self.view.center;
view.backgroundColor = [UIColor orangeColor];
[self.view addSubview:view];