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

//
// 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來實現的
