Graver·学习笔记·绑定事件
前言
从 Graver·学习笔记·入门使用 中可以知道:UIView 是继承自 UIResponder
,从而有了响应触摸事件和分发事件的能力;但是,我们在使用 UIButton、UISlider、UIPageControl 等控件的时候,经常使用 addTarget:action:forControlEvents:
来进行事件绑定,这个是因为它们的父类 UIControl
已经实现好:将触摸事件转化成控件事件,便于平常开发使用。
同样,Graver 中 WMGCanvasControl
也实现了 UIControl
相似地功能,这里我们一起来看看如何使用这个类及其实现细节。
简单使用
个人觉得 WMGCanvasControl
跟 UIControl
类应该相似,不推荐直接使用,应该作为父类提供通用的接口;但是为了演示功能以及断点调试,我们直接直接实例化WMGCanvasControl
, WMGCanvasControl
继承自 UIView
的,这里就直接参考实例 UIView
的方式:
WMGCanvasControl *control = [[WMGCanvasControl alloc] initWithFrame:CGRectMake(100, 100, 200, 200)];
[control addTarget:self action:@selector(controlClick:) forControlEvents:UIControlEventTouchUpInside];
control.backgroundColor = [UIColor redColor];
[self.view addSubview:control];
//点击视图会调用下面方法
- (void)controlClick:(WMGCanvasControl *)control {
NSLog(@"%s %@",__func__,control);
}
效果图如下:
image设计逻辑
WMGCanvasControl
主要是利用 Target-Action 设计模式将触摸事件转换成控件事件。对于 UIControl
中 Target-Action 具体实现可以直接参考:UIKit: UIControl 。
WMGCanvasControl 内部实现逻辑
1、实现 __WMGCanvasControlTargetAction 私有类
__WMGCanvasControlTargetAction
私有类是用来表示一个 Target-Action 的:target 表示接收事件的对象、action 表示事件触发的方法、controlEvents 表示哪种事件。
@interface __WMGCanvasControlTargetAction : NSObject
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL action;
@property (nonatomic, assign) UIControlEvents controlEvents;
@end
2、可变数组 _targetActions 管理对象的事件
利用可变数组来管理对象当前监听的事件,_targetActions 里面是 __WMGCanvasControlTargetAction 对象。
@interface WMGCanvasControl ()
{
NSMutableArray * _targetActions;
}
// 为对象增减监听事件
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
{
if(action)
{
__WMGCanvasControlTargetAction *t = [[__WMGCanvasControlTargetAction alloc] init];
t.target = target;
t.action = action;
t.controlEvents = controlEvents;
[[self _targetActions] addObject:t];
}
}
3、利用 UIResponder 特性将触摸事件转换成控件事件
通过重写 UIResponder 中的触摸事件的方法,将不同触摸事件转换成对应的控件事件,利用 [[UIApplication sharedApplication] sendAction:action to:target from:self forEvent:event]
将事件传递给相应的对象。
//转换简化代码
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIControlEvents currentEvents = UIControlEventTouchDown;
[self _sendActionsForControlEvents:currentEvents withEvent:event];
}
- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents
{
[self _sendActionsForControlEvents:controlEvents withEvent:nil];
}
- (void)sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
[[UIApplication sharedApplication] sendAction:action to:target from:self forEvent:event];
}
知识点
1、weak 来解决 Target-Action 循环引用
这里用 UIKit: UIControl 中的图例来描述为什么会出现循环引用,所以 __WMGCanvasControlTargetAction
中需要用 weak 来修饰 target,来打破循环引用。
疑问
1、WMGCanvasControl 没有解决多次 addTarget 问题
根据代码测试和阅读源码可知,多次调用 addTarget:action:forControlEvents:
方法,会创建多个 __WMGCanvasControlTargetAction
并添加到 _targetActions
中;这里会导致同一事件,对应的方法会被调用多次。
这里应该做去重判断。
2、对于可变数组的读写操作,应该要做多线程安全
总结
这篇我们通过阅读 WMGCanvasControl
源码可以大概知道,如何利用 Target-Action 来将触摸事件转换成控件事件;在实现的过程中,需要分析对象间的持有关系,在必要时用 weak 来打破循环引用。
当然,强烈推荐看文末参考文献,将 UIControl 讲的很透彻。