美文网首页iOS开发攻城狮的集散地
Graver·学习笔记·绑定事件

Graver·学习笔记·绑定事件

作者: pengxuyuan | 来源:发表于2019-01-08 00:15 被阅读3次

    前言

    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

    相关文章

      网友评论

        本文标题:Graver·学习笔记·绑定事件

        本文链接:https://www.haomeiwen.com/subject/oyumrqtx.html