索引
1-解決子視圖越界部分的事件不被觸發(fā)
2-hitTest:withEvent:
3-關(guān)于rect和point的轉(zhuǎn)換總結(jié)
4-rect與point的交集,包含
子視圖越界部分的事件不被觸發(fā)

前提:redView里有blueView和yellowBtn,blueView里有g(shù)reenBtn,
問(wèn)題:greenBtn超出了父視圖blueView的邊界,正常情況下超出區(qū)域的動(dòng)作將失效
【不完善解決方法】:重寫(xiě)方法-pointInside:withEvent:,直接返回YES
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return YES;
}
【拋出潛在問(wèn)題】如果按順序A能解決上述問(wèn)題,但如果按順序B會(huì)導(dǎo)致yellowBtn點(diǎn)擊動(dòng)作被greenBtn所攔截,所以需要優(yōu)化完善方法-pointInside:withEvent:
- (void) setup {
[blueView addSubView:greenBtn];
// 添加子視圖順序-A
[redView addSubView:blueView];
[redView addSubVIew:yellowBtn];
// 添加子視圖順序-B
[redView addSubVIew:yellowBtn];
[redView addSubView:blueView];
}
【完善解決方法】

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
// blueViewde 區(qū)域 || greenBtn超出部分的區(qū)域
if ( CGRectContainsPoint(CGRectMake(0, 0, 200, 120),point) ||
CGRectContainsPoint(CGRectMake(70, 120, 100, 20), point) ) {
return YES;
}
return NO;
}
【原理】順序A和B的區(qū)別,歸因于事件分發(fā)機(jī)制 hit-Testing,其作用是找出屏幕的觸摸點(diǎn)是屬于哪個(gè)view的。
尋找邏輯是:
先父視圖后子視圖;
最后添加的子視圖到先添加的子視圖;
遞歸尋找,直到找到view。
分別說(shuō)明順序A和B:
A:
1-檢查redView,發(fā)現(xiàn)觸摸點(diǎn)在redView內(nèi),繼續(xù)檢查子視圖yellowView
2-如果發(fā)現(xiàn)觸摸點(diǎn)在yellowView內(nèi),繼續(xù)遞歸檢查yellowView
3-如果發(fā)現(xiàn)觸摸點(diǎn)不在yellowView內(nèi),繼續(xù)遞歸檢查blueView
B:
1-檢查redView,發(fā)現(xiàn)觸摸點(diǎn)在redView內(nèi),繼續(xù)檢查子視圖blueView
2-如果發(fā)現(xiàn)觸摸點(diǎn)在blueView內(nèi),繼續(xù)遞歸檢查blueView
3-如果發(fā)現(xiàn)觸摸點(diǎn)不在blueView內(nèi),繼續(xù)遞歸檢查blueView
- hitTest:withEvent:
// 可以根據(jù)自己的需求,重寫(xiě)方法
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
[super hitTest:point withEvent:event];
// Determine whether you can receive touch events
if ( self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01 ) {
return nil;
}
// Determine if the touch point is out of reach
if ( ![self pointInside:point withEvent:event] ) {
return nil;
}
NSInteger count = self.subviews.count;
for ( NSInteger i = count - 1; i >= 0; i-- ) {
UIView *childView = self.subviews[i];
CGPoint childPoint = [self convertPoint:point toView:childView];//
UIView *fitView = [childView hitTest:childPoint withEvent:event];
if ( fitView ) {
return fitView;
}
}
return self;
}
關(guān)于rect和point的轉(zhuǎn)換總結(jié)
toView就是從左往右開(kāi)始讀代碼,也是從左往右理解意思
fromView就是從右往左開(kāi)始讀代碼,也是從右往左理解意思
// rect
CGRect newRect = [self.yellowView convertRect:self.yellowView.bounds toView:nil];
在yellowView中,有個(gè)和yellowView一樣的大小的區(qū)域bounds,此區(qū)域相對(duì)于window的位置
CGRect newRect = [self.greyView convertRect:self.yellowView.frame toView:nil];
在greyView中,其子視圖yellowView的frame區(qū)域,相對(duì)于window的位置
CGRect rec1 = [self.view.window convertRect:self.yellowView.bounds fromView:self.yellowView];
在yellowView中,有個(gè)和yellowView一樣大小的區(qū)域bounds,此區(qū)域相對(duì)于window的位置
上述三種方式,有助于理解frame和bounds的區(qū)別
// point
A(CGPoint)convertPoint:B(CGPoint)point toView:C(nullableUIView *)view;
A區(qū)域里面有個(gè)坐標(biāo)B,需要把相對(duì)于A(yíng)的坐標(biāo)B轉(zhuǎn)換成相對(duì)于C的坐標(biāo)A(CGPoint)convertPoint:B(CGPoint)point fromView:C(nullableUIView *)view;
從C區(qū)域里面轉(zhuǎn)換坐標(biāo)B,需要把相對(duì)于C的坐標(biāo)轉(zhuǎn)換成相對(duì)于A(yíng)的坐標(biāo)
rect與point的交集,包含
CGRectContainsRect(<#CGRect rect1#>, <#CGRect rect2#>)
表示rect1和rect2是否相交
CGRectContainsPoint(<#CGRect rect#>, <#CGPoint point#>)
表示rect中是否包含point坐標(biāo)
CGRectIntersectsRect(<#CGRect rect1#>, <#CGRect rect2#>)
表示rect1中是否包含rect2