ios 視圖越界與事件觸發(fā)

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

子視圖越界部分的事件不被觸發(fā)

hitTest001.png

前提: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];
}

【完善解決方法】


hitTest001.png
- (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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容