响应链 --- 被很多人忽视的另一种数据交互方式

2018-01-23  本文已影响94人  moxacist

前言

这篇文章有些是借鉴了其他博客文章的知识点加以理解的,在最下方我会列出参考的链接。有兴趣的可以点进去看下。

主要内容

本篇文章的主要内容有响应链的构成、响应链的传递过程、hit:test判定流程、通过响应链进行交互传递。

1.响应链的构成

从名字中开始理解,响应链是由一系列响应者通过某种联系构成的链条。

2.响应链的传递

当界面上的一个button被点击时

不能响应的原因 :

响应链层级 传递图

3.hit:test流程

刚才我们说到响应者需要将事件在响应链上传递,在内部是通过两个方法进行传递 pointinside 和hit test

1.首先对响应链最底层的View(UIWindow)视图进行命中判定,调用hittest:withevent方法
2.如果判定不是该视图,则返回nil,pointInside:withevent方法无效,如果命中,则根据pointInside, 返回是不是响应区域,如果是,则遍历该视图的子视图进行hittest判定
3.重复2,知道找到最上层最合适的视图,然后执行事件

这张图是盗的
假设用户点击了视图E

1.首先touch会命中window,查出上级视图是viewA
2.viewA判定在A中,然后向上遍历子视图B和C
3.B判定不在自己的视图内,返回nil,B分支结束
4.C判定在自己的视图内,向上遍历D和E进行判定
5.D判定不在视图内,D分支结束
6.E判定在该视图内,子视图为nil,则E是最合适视图

  1. E进行响应,如果E无法响应,则执行父视图C的响应

以上就是响应链传递的流程,说完了流程现在来说说响应链具体有何应用

应用一: 超区或者裁区响应
image.png

对于这张图片,一般简单做法就是方形视图切圆角,这样的话圆形周边的四个角(红色区域)也会响应,如果交互或者产品要求只能圆形响应的话,这时候就需要重写方法

#import "EDHSqurView.h"

@implementation EDHSqurView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    NSLog(@"%s",__func__);
    UIView *vuew = [super hitTest:point withEvent:event];
    return vuew;
}

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    NSLog(@"%s",__func__);
    //已经知道是个正方形
    CGFloat radius = self.frame.size.width/2;
   
    UIBezierPath *bezier = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius)
                                                          radius:radius
                                                      startAngle:0 endAngle:M_PI*2
                                                       clockwise:1];
    return CGPathContainsPoint(bezier.CGPath, NULL, point, NO);
    
}

@end

这段代码要用到的是下面那个方法,进行裁剪交互区域,返回可交互的圆形区域

按钮超区.png

这段图片也是进行超区处理

应用二:通过响应链进行对象间交互
目前常用的数据/事件回传有block、delegate、notification,各有所长。响应链具有逐层传递的特点,可通过nextresponder取到下一个响应者,基于这个特点,可以设计通过链路进行对象的数据回传

具体做法

下层响应者接受多个响应事件的strategy模式

当下层响应者例如controller接受来自多个子视图的事件时,会导致判断的if else冗余,这时可以采用strategy对每个eventname分类处理

解决办法:定义一个字典,eventname做key,invocation做value
在routeEvent里面通过eventname取出strategy字典的value - invocation,
然后[invocation invoke]

这种方式要求每个eventname都对应一个selector,如果event不是很多的话,
用if else 也没什么关系
如下图


定义event字典 事件调用的地方 image.png
参考文章
demo链接: https://github.com/MoonMD/ResponderDemo.git
上一篇 下一篇

猜你喜欢

热点阅读