前言
這篇文章有些是借鑒了其他博客文章的知識(shí)點(diǎn)加以理解的,在最下方我會(huì)列出參考的鏈接。有興趣的可以點(diǎn)進(jìn)去看下。
主要內(nèi)容
本篇文章的主要內(nèi)容有響應(yīng)鏈的構(gòu)成、響應(yīng)鏈的傳遞過程、hit:test判定流程、通過響應(yīng)鏈進(jìn)行交互傳遞。
1.響應(yīng)鏈的構(gòu)成
從名字中開始理解,響應(yīng)鏈?zhǔn)怯梢幌盗许憫?yīng)者通過某種聯(lián)系構(gòu)成的鏈條。
響應(yīng)者:具有響應(yīng)和處理事件能力的對(duì)象?;愂荱IResponder=
鏈條連接器:pointInside 和hit:test方法
-
響應(yīng)者類關(guān)系圖
響應(yīng)者類關(guān)系圖.png
2.響應(yīng)鏈的傳遞
當(dāng)界面上的一個(gè)button被點(diǎn)擊時(shí)
- 1.系統(tǒng)會(huì)封裝一個(gè)UIEvent對(duì)象傳遞到Appdelegate
- 2.由Appdelegate向Application、UIWindow等向上傳遞事件
- 3.視圖收到父響應(yīng)者傳遞的事件后,通過hitView判斷是否選中
- 4.如果沒有判定中,那么不處理,如果判定中,遍歷子視圖,遞歸執(zhí)行3和4
- 5.最后遞歸找到最頂層的button,最適合的視圖
- 6.如果能響應(yīng),執(zhí)行它的交互響應(yīng) ,不能的話,遞歸到上一個(gè)響應(yīng)者,執(zhí)行響應(yīng) 。
- 7.如果一直遞歸到appdelegate到都沒響應(yīng),則廢棄
不能響應(yīng)的原因 :
- view.hidden = YES
- view.alpha<0.01
- view.userinterface = NO
- 不添加交互事件并不是不能響應(yīng),只是默認(rèn)不做處理


3.hit:test流程
剛才我們說到響應(yīng)者需要將事件在響應(yīng)鏈上傳遞,在內(nèi)部是通過兩個(gè)方法進(jìn)行傳遞 pointinside 和hit test
1.首先對(duì)響應(yīng)鏈最底層的View(UIWindow)視圖進(jìn)行命中判定,調(diào)用hittest:withevent方法
2.如果判定不是該視圖,則返回nil,pointInside:withevent方法無效,如果命中,則根據(jù)pointInside, 返回是不是響應(yīng)區(qū)域,如果是,則遍歷該視圖的子視圖進(jìn)行hittest判定
3.重復(fù)2,知道找到最上層最合適的視圖,然后執(zhí)行事件

假設(shè)用戶點(diǎn)擊了視圖E
1.首先touch會(huì)命中window,查出上級(jí)視圖是viewA
2.viewA判定在A中,然后向上遍歷子視圖B和C
3.B判定不在自己的視圖內(nèi),返回nil,B分支結(jié)束
4.C判定在自己的視圖內(nèi),向上遍歷D和E進(jìn)行判定
5.D判定不在視圖內(nèi),D分支結(jié)束
6.E判定在該視圖內(nèi),子視圖為nil,則E是最合適視圖
- E進(jìn)行響應(yīng),如果E無法響應(yīng),則執(zhí)行父視圖C的響應(yīng)
以上就是響應(yīng)鏈傳遞的流程,說完了流程現(xiàn)在來說說響應(yīng)鏈具體有何應(yīng)用
應(yīng)用一: 超區(qū)或者裁區(qū)響應(yīng)

對(duì)于這張圖片,一般簡(jiǎn)單做法就是方形視圖切圓角,這樣的話圓形周邊的四個(gè)角(紅色區(qū)域)也會(huì)響應(yīng),如果交互或者產(chǎn)品要求只能圓形響應(yīng)的話,這時(shí)候就需要重寫方法
#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__);
//已經(jīng)知道是個(gè)正方形
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
這段代碼要用到的是下面那個(gè)方法,進(jìn)行裁剪交互區(qū)域,返回可交互的圓形區(qū)域

這段圖片也是進(jìn)行超區(qū)處理
應(yīng)用二:通過響應(yīng)鏈進(jìn)行對(duì)象間交互
目前常用的數(shù)據(jù)/事件回傳有block、delegate、notification,各有所長(zhǎng)。響應(yīng)鏈具有逐層傳遞的特點(diǎn),可通過nextresponder取到下一個(gè)響應(yīng)者,基于這個(gè)特點(diǎn),可以設(shè)計(jì)通過鏈路進(jìn)行對(duì)象的數(shù)據(jù)回傳
具體做法
- 1.通過UIResponder的一個(gè)category,定義一個(gè)方法,通過響應(yīng)鏈向底層傳遞
- 2.通過extern nsstring const 方式定義交互事件的eventname名字(也可以硬編碼)
- 3.以tableview為例,cell上有多個(gè)交互事件,在button的點(diǎn)擊事件里調(diào)用category方法
-
4.在controller里面接收到上一層響應(yīng)者發(fā)來的信息,處理,若需要繼續(xù)傳遞(或穿透?jìng)鬟f) 則調(diào)用category的方法
1-定義分類
2-定義eventname
3-在交互事件是執(zhí)行category
4-在控制器里的操作
下層響應(yīng)者接受多個(gè)響應(yīng)事件的strategy模式
當(dāng)下層響應(yīng)者例如controller接受來自多個(gè)子視圖的事件時(shí),會(huì)導(dǎo)致判斷的if else冗余,這時(shí)可以采用strategy對(duì)每個(gè)eventname分類處理
解決辦法:定義一個(gè)字典,eventname做key,invocation做value
在routeEvent里面通過eventname取出strategy字典的value - invocation,
然后[invocation invoke]
這種方式要求每個(gè)eventname都對(duì)應(yīng)一個(gè)selector,如果event不是很多的話,
用if else 也沒什么關(guān)系
如下圖


-
響應(yīng)鏈傳遞和代理block等的使用對(duì)比





