iOS开发攻城狮的集散地

Graver·学习笔记·绑定事件

2019-01-08  本文已影响3人  pengxuyuan

前言

Graver·学习笔记·入门使用 中可以知道:UIView 是继承自 UIResponder,从而有了响应触摸事件和分发事件的能力;但是,我们在使用 UIButton、UISlider、UIPageControl 等控件的时候,经常使用 addTarget:action:forControlEvents: 来进行事件绑定,这个是因为它们的父类 UIControl 已经实现好:将触摸事件转化成控件事件,便于平常开发使用。

同样,Graver 中 WMGCanvasControl 也实现了 UIControl 相似地功能,这里我们一起来看看如何使用这个类及其实现细节。

简单使用

个人觉得 WMGCanvasControlUIControl 类应该相似,不推荐直接使用,应该作为父类提供通用的接口;但是为了演示功能以及断点调试,我们直接直接实例化WMGCanvasControlWMGCanvasControl 继承自 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,来打破循环引用。

image

疑问

1、WMGCanvasControl 没有解决多次 addTarget 问题

根据代码测试和阅读源码可知,多次调用 addTarget:action:forControlEvents: 方法,会创建多个 __WMGCanvasControlTargetAction 并添加到 _targetActions 中;这里会导致同一事件,对应的方法会被调用多次。

这里应该做去重判断。

2、对于可变数组的读写操作,应该要做多线程安全

总结

这篇我们通过阅读 WMGCanvasControl 源码可以大概知道,如何利用 Target-Action 来将触摸事件转换成控件事件;在实现的过程中,需要分析对象间的持有关系,在必要时用 weak 来打破循环引用。

当然,强烈推荐看文末参考文献,将 UIControl 讲的很透彻。

参考文献

UIKit: UIControl

上一篇下一篇

猜你喜欢

热点阅读