1. 程式人生 > >ios 超出父控制元件區域支援點選事件

ios 超出父控制元件區域支援點選事件

標題中的需求其實常常能遇到,如下圖


圖 1

當按鈕超出Tab bar的view後,那麼其實按鈕超出的部分是無法被點選的。那麼先來說說解決辦法

1.我們重寫藍色view的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event的方法

  - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{
    //if內的條件應該為,當觸控點point超出藍色部分,但在黃色部分時
    if (.....){
     return YES;
    }
    return NO;
  }

那麼以上為什麼能解決方法?

這和iOS的事件分發機制 hit-Testing有關,簡單的說,hit-Testing的作用就是找出你每次觸控式螢幕幕,點到的究竟是哪個view。

比如以下這個圖


圖 2

當我去點選View-C的時候,hit-Testing實際上是這樣檢測的
1.首先,檢視會先從View-A開始檢查,發現觸控點在View-A,所以檢查View-A的子檢視View-B。
2.發現觸控點在View-B內,好棒!看看View-B內的子檢視View-C。
3.發現觸控點在View-C內,但View-C沒有子檢視了,所以View-C是此次觸控事件的hit-TestView了。

那麼UIView中其實提供了兩個方法來確定hit-TestView
1.- (UIView )hitTest:(CGPoint)point withEvent:(UIEvent 

)event;
2.- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;//這個就是我們上面重寫的方法

注意其實在每次遞迴去呼叫hitTest:(CGPoint)point withEvent:(UIEvent *)event之前,都會呼叫pointInside:withEvent:來確定該觸控點是否在該View內。

所以當我們重寫pointInside:(CGPoint)point withEvent:(UIEvent *)event後,其實我們的點選後呼叫hitTest來遞迴的找hit-TestView的區域從這樣:

圖 3
變成了這樣:

圖 4

這樣當我們愉快的點選上半凸起的區域時,hit-Testing便回去檢查藍色檢視內的子檢視,即黃色區域。從而來完成此次觸控事件。
Enjoy :)

2017年3月1日更新

針對gwk_ios提問,我首先表示感謝,我們有必要把這個問題拿出來,研究一下。
讓我們先看個圖:


圖 5

恩,這圖似乎有點大。不過沒事。
gwk_iost提出了2個問題

如果- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 方法只要return yes 就能點選超出父view邊界的子view上的button呢

這樣做實際上會有一個問題,如上圖,如果在View A中,先新增View B(包含了Button A),再新增Button B,那麼再點選Button B時,Button B的方法會被正確觸發。
但是,[如果在View A中,先新增Button B,再新增View B(包含了Button A)],這個時候,你會發現,點選Button B時,方法無法被正確觸發了。因為你在點選View A內的任意位置時,系統會優先呼叫View B 的pointInSide: WithEvent:方法,這時候你返回YES,就截斷了事件,包括點選Button B。

那麼 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event 內if判斷該怎麼寫呢

其實point會返回的座標是基於該View的座標系(所以超出該View時,可能會出現負數),以iphone 7的螢幕大小為例子:


圖 6
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{

    if (CGRectContainsPoint(CGRectMake(0, 0, 87.5, 100),point)||CGRectContainsPoint(CGRectMake(87.5, -100, 200, 200), point)||CGRectContainsPoint(CGRectMake(287.5, 0, 87.5, 100),point)) {
        return YES;
    }
    return NO;
}

Enjoy :)