作為iOS開發(fā)者,相應(yīng)鏈大家理解的每個人都不太一樣,下面陳述下我理解的iOS相應(yīng)鏈機制是什么樣的?可能對,也可能不對,不對的還望理解更透徹的人給予指點(感謝)!
首先說下為什么會有相應(yīng)鏈這個東西,顧名思義,響應(yīng)鏈,肯定是響應(yīng)你行為操作的一個次序鏈條吧!響應(yīng)鏈我理解的其實是兩個步驟
1、 首先當(dāng)你手觸摸到屏幕的時候,iOS系統(tǒng)會產(chǎn)生一個需要處理的事件,這個事件就是被封裝成UIEvent對象,然后放到系統(tǒng)的任務(wù)隊列里邊去處理,傳遞,查找出當(dāng)前手是觸目的那個UIView,這個過程,是響應(yīng)鏈的事件傳遞,也是視圖的查找,查找是從視圖的最底層也就是視圖最下邊的父視圖開始依次向上傳遞查找, 這里要說的查找的時候要說的兩個方法,第一個方法作用是返回最上層的試圖,也就是子孫試圖中,輩分最小的那個,第二個方法是判斷當(dāng)前點在不在改視圖接受著也就是bounds的區(qū)域內(nèi),不在的話,該視圖對應(yīng)的hitTest方法返回肯定是空,也就不會再去遍歷PointInside 返回為NO的視圖上的子試圖了
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event; // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event; // default returns YES if point is in bounds
首先結(jié)合下邊這個視圖來介紹下這兩個方法
舉個例子比如
我現(xiàn)在分別在自定義的 A、B、C、D、E這幾個自定義的UIView 里邊重寫了
- -(nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)even
-
(BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event
這兩個方法 然后看他們的打印情況
我現(xiàn)在就舉中一個例子
當(dāng)你手點擊E視圖的時候 首先我們來先看下打印結(jié)果吧:
image.png
-
結(jié)果是AHitese --->APoint --->CHitest --->CPoint ->EHitTest ---->Epoint 然后找到了E才執(zhí)行了E的、C、A的hitest的返回View
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event響應(yīng)方法
這里方法打印了兩次應(yīng)該是我在重寫放的內(nèi)部調(diào)用 [super _cmd] 對應(yīng)的方法吧,具體愿意不知道 猜想的,但是順序我們知道了 然后再點擊A的結(jié)果和B的結(jié)果,分別得到的經(jīng)驗就是遍歷view上的所有子試圖,但是他們有層級結(jié)構(gòu),比如這里的A是父視圖,B、C、D是同級的A的子試圖,E視圖是最底層C的子試圖,所以我從點擊打印的結(jié)果得到的結(jié)論是 如果你點擊了A
那么他們調(diào)用各自的HitTest的方法順是 A最先,然后再調(diào)用A下邊的子試圖,但是A的子試圖也是有優(yōu)先級別的,這調(diào)用的級別規(guī)律是符合出棧入棧規(guī)律的,相同級別情況下,后添加的先調(diào)用的規(guī)律

也就是說調(diào)用hitTest 無論返回的UIView是否存在,都再會調(diào)用PointInside方法的,然后通過PointInside方法返回的YES和NO再去判斷用不用調(diào)用子試圖的HitTest方法,如果返回YES調(diào)用,recursively(系統(tǒng)APi說了)遞歸調(diào)用子試圖的Hittest方法,如果返回NO就不在調(diào)用子試圖的HitTest方法 也就不在調(diào)用poinsinside方法,

#import "CustomView.h"
#import "CustomBtn.h"
@interface CustomView()
@property (strong, nonatomic) IBOutlet UIView *contentView;
@property(nonatomic,strong)CustomBtn *customBtn;
@end
@implementation CustomView
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.customBtn = [[CustomBtn alloc] initWithFrame:CGRectMake(50, -50, 100, 100)];
_customBtn.backgroundColor = [UIColor redColor];
[self addSubview:self.customBtn];
[_customBtn addTarget:self action:@selector(isinAction) forControlEvents:(UIControlEventTouchUpInside)];
_customBtn.bounds = UIEdgeInsetsInsetRect(self.customBtn.bounds, UIEdgeInsetsMake(-3, -14, -5, -16));//擴寬可點區(qū)域
NSLog(@"button frame = %@",NSStringFromCGRect(_customBtn.frame));
}
return self;
}
-(void)isinAction{
NSLog(@"Hello Kit");
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(150, 0, 10, 10)];
label.backgroundColor = [UIColor greenColor];
[self.window addSubview:label];
[UIView animateWithDuration:0.5 animations:^{
label.frame = CGRectMake(150, 200, 10, 10);
} completion:^(BOOL finished) {
[label removeFromSuperview];
}];
}
-(UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
CGPoint newPoint = [self convertPoint:point toView:self.customBtn];
BOOL isINside = [self.customBtn pointInside:newPoint withEvent:event];
if (isINside) {
return self.customBtn;
}else{
[super hitTest:point withEvent:event];
}
NSLog(@"進(jìn)入C_View---hitTest withEvent ---");
UIView * view = [super hitTest:point withEvent:event];
NSLog(@"離開C_View---hitTest withEvent ---hitTestView:%@",view);
return view;
}
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
NSLog(@"C_view---pointInside withEvent ---");
BOOL isInside = [super pointInside:point withEvent:event];
NSLog(@"C_view---pointInside withEvent --- isInside:%d",isInside);
return isInside;
}
