iOS开发-RunTime的简单应用

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];

demo地址

你可能感兴趣的:(iOS开发-RunTime的简单应用)