iOS 如何更方便的給控制元件新增Action?

zhf_Zachariah發表於2018-01-25

做iOS開發的都知道UIButton的繼承關係,如下 UIButton-->UIControl-->UIView-->UIResponder-->NSObject 這個控制元件的建立設定以及用法就不多說了,下面說說今天的重點。以UIButton為例,平時在用UIButton的時候,給當前button新增事件處理方法就是呼叫它的addTarget:<#(nonnull id)#> action:<#(nonnull SEL)#> events:<#(customButtonType)#>,然後再在方法裡完成對事件的處理。但是這樣寫的話,程式碼的閱讀性就降低了很多,那怎麼寫才能在建立這個button地方順帶將它的事件處理方法就敲了呢?

為了不再重新寫個方法來破壞程式碼可讀性,那就呼叫一個帶有block的方法來完成對事件處理方法的回撥。那怎麼才能呼叫這個方法呢?Category。

UIButton的Category
但是給UIButton新增之後Category之後不可能就只寫我們這一次要做的事件處理啊,怎麼辦?畢竟是給button新增方法,每個button都有不同的事件處理,這個時候Runtime就能幫我們一個大忙-動態繫結。具體實現: .h

//
//  UIButton+block.h
//  RuntimeTest
//
//  Created by Hongfei Zhai on 2017/11/22.
//  Copyright © 2017年 Hongfei Zhai. All rights reserved.
//

#import <UIKit/UIKit.h>
//使用runtime呼叫的庫
#import <objc/runtime.h>

typedef void(^TouchBlock)(UIButton *sender);

@interface UIButton (block)

- (void)touchWithEvents:(UIControlEvents )controlEvents withBlock:(TouchBlock)touchBlock;

@end
複製程式碼

.m

//
//  UIButton+block.m
//  RuntimeTest
//
//  Created by Hongfei Zhai on 2017/11/22.
//  Copyright © 2017年 Hongfei Zhai. All rights reserved.
//

#import "UIButton+block.h"

static const void *TouchKey = &TouchKey;

@implementation UIButton (block)

- (void)touchWithEvents:(UIControlEvents)controlEvents withBlock:(TouchBlock)touchBlock {
    //通過一個key繫結block到相對應的button
    objc_setAssociatedObject(self, TouchKey, touchBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
    //在這裡新增action
    [self addTarget:self action:@selector(buttonClick:) forControlEvents:(controlEvents)];
}

- (void)buttonClick:(UIButton *)sender {
    // 通過key獲取到對應button的block回撥
    TouchBlock touchblock = objc_getAssociatedObject(sender, TouchKey);
    if (touchblock) {
        touchblock(sender);
    }
}

@end
複製程式碼

使用

    UIButton *button = [UIButton buttonWithType:(UIButtonTypeCustom)];
    button.frame = CGRectMake(0, 0, 100, 50);
    button.center = self.view.center;
    button.backgroundColor = [UIColor redColor];
    button.tag = 10001;
    [button touchWithEvents:(UIControlEventTouchUpInside) withBlock:^(UIButton *sender) {
        NSLog(@"sender.tag === %ld",(long)sender.tag);
    }];
    [self.view addSubview:button];
複製程式碼

只用UIButton做了一個例子,通過它的繼承關係可以看出控制元件很多都可以新增block來回撥做出事件的處理。底下用View來做出一個類似button的功能控制元件來說明target-action核心機制:

//
//  Test_Button.h
//  RuntimeTest
//
//  Created by Hongfei Zhai on 2017/11/22.
//  Copyright © 2017年 Hongfei Zhai. All rights reserved.
//

#import <UIKit/UIKit.h>

typedef enum customButtonType{
    
    customButtonTypeTouchDown,//按下響應
    customButtonTypeTouchUpInside//抬起響應
    
}customButtonType;


@interface Test_Button : UIView


- (void)addTarget:(nonnull id)target action:(nonnull SEL)action events:(customButtonType)eventsType;


@end
複製程式碼
//
//  Test_Button.m
//  RuntimeTest
//
//  Created by Hongfei Zhai on 2017/11/22.
//  Copyright © 2017年 Hongfei Zhai. All rights reserved.
//

#import "Test_Button.h"

@interface Test_Button ()
{
    id myTarget;//執行按鈕回撥方法的物件
    SEL myAction;//按鈕的回撥方法
}

@property (nonatomic,assign) customButtonType btnType;//觸發按鈕回撥方法的列舉值

@end

@implementation Test_Button

//新增點選事件的方法
- (void)addTarget:(id)target action:(nonnull SEL)action events:(customButtonType)eventsType{
    self.btnType = eventsType;
    myTarget = target;
    myAction = action;
}
//手指觸碰按鈕,呼叫此方法
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //呼叫按鈕的type為touchDown的時候在呼叫按鈕的回撥方法,如果不是,就不能呼叫按鈕的回撥
    if (self.btnType == customButtonTypeTouchDown) {
        //呼叫按鈕的回撥方法 目標動作機制的核心 object - 回撥方法的引數
        [myTarget performSelector:myAction withObject:self];
    }
    
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    if (self.btnType == customButtonTypeTouchUpInside) {
        //呼叫按鈕的回撥方法 目標動作機制的核心 object - 回撥方法的引數
        [myTarget performSelector:myAction withObject:self];
    }
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

}
@end

複製程式碼

用過BlocksKit都知道其中給控制元件新增block回撥的機制也是通過runtime來實現的

BlocksKit新增事件處理的實現
當然BlocksKit功能強大的多,邏輯也更加縝密,原理還是這個原理。而Runtime的使用會有出乎意料的幫助。

相關文章