前言
這篇文章有些是借鑒了其他博客文章的知識點加以理解的,在最下方我會列出參考的鏈接。有興趣的可以點進去看下砂客。
主要內容
本篇文章的主要內容有響應鏈的構成、響應鏈的傳遞過程呵恢、hit:test判定流程鞠值、通過響應鏈進行交互傳遞。
1.響應鏈的構成
從名字中開始理解瑰剃,響應鏈是由一系列響應者通過某種聯(lián)系構成的鏈條齿诉。
響應者:具有響應和處理事件能力的對象∩我Γ基類是UIResponder=
鏈條連接器:pointInside 和hit:test方法
-
響應者類關系圖
2.響應鏈的傳遞
當界面上的一個button被點擊時
- 1.系統(tǒng)會封裝一個UIEvent對象傳遞到Appdelegate
- 2.由Appdelegate向Application粤剧、UIWindow等向上傳遞事件
- 3.視圖收到父響應者傳遞的事件后,通過hitView判斷是否選中
- 4.如果沒有判定中挥唠,那么不處理抵恋,如果判定中,遍歷子視圖宝磨,遞歸執(zhí)行3和4
- 5.最后遞歸找到最頂層的button弧关,最適合的視圖
- 6.如果能響應盅安,執(zhí)行它的交互響應 ,不能的話世囊,遞歸到上一個響應者别瞭,執(zhí)行響應 。
- 7.如果一直遞歸到appdelegate到都沒響應株憾,則廢棄
不能響應的原因 :
- view.hidden = YES
- view.alpha<0.01
- view.userinterface = NO
- 不添加交互事件并不是不能響應蝙寨,只是默認不做處理
3.hit:test流程
剛才我們說到響應者需要將事件在響應鏈上傳遞,在內部是通過兩個方法進行傳遞 pointinside 和hit test
1.首先對響應鏈最底層的View(UIWindow)視圖進行命中判定嗤瞎,調用hittest:withevent方法
2.如果判定不是該視圖墙歪,則返回nil,pointInside:withevent方法無效贝奇,如果命中虹菲,則根據(jù)pointInside, 返回是不是響應區(qū)域,如果是掉瞳,則遍歷該視圖的子視圖進行hittest判定
3.重復2毕源,知道找到最上層最合適的視圖,然后執(zhí)行事件
假設用戶點擊了視圖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是最合適視圖
- E進行響應,如果E無法響應塌忽,則執(zhí)行父視圖C的響應
以上就是響應鏈傳遞的流程拍埠,說完了流程現(xiàn)在來說說響應鏈具體有何應用
應用一: 超區(qū)或者裁區(qū)響應
對于這張圖片,一般簡單做法就是方形視圖切圓角土居,這樣的話圓形周邊的四個角(紅色區(qū)域)也會響應枣购,如果交互或者產(chǎn)品要求只能圓形響應的話,這時候就需要重寫方法
#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)知道是個正方形
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
這段代碼要用到的是下面那個方法擦耀,進行裁剪交互區(qū)域棉圈,返回可交互的圓形區(qū)域
這段圖片也是進行超區(qū)處理
應用二:通過響應鏈進行對象間交互
目前常用的數(shù)據(jù)/事件回傳有block、delegate眷蜓、notification分瘾,各有所長。響應鏈具有逐層傳遞的特點吁系,可通過nextresponder取到下一個響應者德召,基于這個特點白魂,可以設計通過鏈路進行對象的數(shù)據(jù)回傳
具體做法
- 1.通過UIResponder的一個category,定義一個方法上岗,通過響應鏈向底層傳遞
- 2.通過extern nsstring const 方式定義交互事件的eventname名字(也可以硬編碼)
- 3.以tableview為例福荸,cell上有多個交互事件,在button的點擊事件里調用category方法
-
4.在controller里面接收到上一層響應者發(fā)來的信息液茎,處理逞姿,若需要繼續(xù)傳遞(或穿透傳遞) 則調用category的方法
下層響應者接受多個響應事件的strategy模式
當下層響應者例如controller接受來自多個子視圖的事件時,會導致判斷的if else冗余捆等,這時可以采用strategy對每個eventname分類處理
解決辦法:定義一個字典,eventname做key续室,invocation做value
在routeEvent里面通過eventname取出strategy字典的value - invocation栋烤,
然后[invocation invoke]
這種方式要求每個eventname都對應一個selector,如果event不是很多的話挺狰,
用if else 也沒什么關系
如下圖
-
響應鏈傳遞和代理block等的使用對比