這幾天遇到一個問題,就是有個效果要穿透tableview(背景色是clear,設置了inset,所以可以看到下面的其他視圖),這個時候雖然可以看到背后的視圖,但是無法點擊,思考了很長時間,最后想到了通過響應者鏈條來實現這一效果.
簡單的說一下響應者鏈條,拿一個簡單的點擊事件來說,為什么當你點擊一個button它會有響應事件,這個就是響應者鏈條來確定的它就是響應對象,大家知道所有的視圖和控制器都是響應者對象,因為他們都繼承自UIResponder這個類,每當有一個點擊事件,它會從最下面(window)的視圖或者控制器開始查詢,會遍歷它的子視圖,按添加的先后順序倒序開始查詢,為什么是倒序呢?因為你最后一個添加的視圖是在最上面,一般情況下,你點擊的視圖是你看到的視圖,也就是放在最上面的視圖,所以倒序開始排查,若果發現某一個子視圖包含了這個時間(可以理解為點擊的點在這個子視圖上),系統會繼續遍歷子視圖的子視圖,繼續按上面的方法開始排查,直到某一個視圖它沒有子視圖,或者點擊的點沒有落在他的子視圖的范圍內,這個時候,就把自己作為最終的響應者(第一響應者)返回,這個就是大體的響應者鏈條的原理,詳細的原理理解大家可以上網搜索"響應者鏈條",會有很多解釋,這里就不太多的解釋了.
今天主要是用到了,響應者鏈條中的一個重要的方法- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event ,這個方法是響應者鏈條能夠實現的底層依托,每一個繼承自uiresponder的類都會有這個方法,源碼的實現如下:
```
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!self.isUserInteractionEnabled || self.isHidden || self.alpha <= 0.01) {
return nil;
}
if ([self pointInside:point withEvent:event]) {
for (UIView *subview in [self.subviews reverseObjectEnumerator]) {
CGPoint convertedPoint = [subview convertPoint:point fromView:self];
UIView *hitTestView = [subview hitTest:convertedPoint withEvent:event];
if (hitTestView) {
return hitTestView;
}
}
}
return self;
}
```
大家可以看著這個代碼理解響應者鏈條,而解決我們今天的問題,只需要修改一個地方就可以了,把最后的return self 改成 return nil ,就可以了,雖然簡單,但是需要理解為什么這樣改還有在哪一個view里面改.
首先,我們來確定是修改哪一個view的這個方法,因為我們想要tableView可以被穿透,所以我們定位到tableView上面,來重寫UITableView的- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法(分類實現).
其次,需求:"只是inset部分由于tableView的顏色是透明的,可以看到后面的按鈕,需要點擊按鈕的時候有響應事件,但是正常的tableViewCell,并不會有穿透效果",所以如果tableView的子視圖(包括cell,一級其他的子視圖)有是第一響應者的,還是需要正常的返回,但是如果最后是tableView本身返回,說明你點擊的是inset部分,這個時候我們需要按鈕來響應時間,所以當tableView本身被返回的時候,我們返回nil,這個時候他的上一級接收到nil后,會繼續執行其他的子視圖,而tableView這個子視圖就被跳過了,從而實現了tableView的穿透效果.
好了,到這里問題被很好的解決,可以開心的回家吃飯了.