怎樣選擇最合適處理事件的控件
當用戶點擊屏幕後,產生2個參數 一個 touches 保存用戶點擊的 UITouch對象到 NSSet 中,和一個事件對象UIEvent ,並且加入到UIApplication 對象的事件處理Loop中隊列
由UIApplication查看,當前有沒有堵塞,如果沒有就將事件分發下去,一般是交給主窗口,keyWindow
然後Window 會用 下面的這個方法
//Swift
func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView?
//Objective-C
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- 該方法是用於查詢當前最佳處理事件的控件,如果我們重寫window的這個方法後,其他view的觸摸事件沒受影響,那麼說明我們的思路是對的,廢話不多說,代碼帶註釋
Swift
override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
/*1.判斷自己可不可以與用戶交互滿足一下幾點
1.self.userInteractionEnabled = YES 可以與用戶交互
2.self.hidden = NO 不是隱藏的
3.self.alpha > 0.01 透明度大於 0.01 也就是用戶可見
*/
//下面是寫的上面的反面
if self.userInteractionEnabled == false || self.hidden == true || self.alpha <= 0.01 {
return nil
}
/*2.這裏查看這個點是不是在控件的bound上了,如果不在就返回nil */
if self.pointInside(point, withEvent: event) == false { return nil }
/*3.遍歷 子控件,由於addSubView 本身是把圖層加到另
一個圖層上面,導致圖層的順序是最後添加的在最上面,也就是
上面說的,後面加入的控件可能會在蓋住,前面加入的控件圖
層,而響應鏈是優先最上面的圖層的,考慮到這個遞歸算法是
DFS 也就是深度優先,所以必須從後面的子控件遍歷
*/
//3.1得到子控制總個數
let count = Int(self.subviews.count) - 1
for var i = count ; i >= 0 ; --i {
//1.得到子控件
let childView:UIView = self.subviews[i]
/*2,將父控件的座標點轉換到子控件中的形式,其實就拿
子控件在父控件的frame的x,y ,與用戶觸點的父控件的
x,y,進行減法*/
let childPoint = self.convertPoint(point, toView: childView)
//3.繼續遞歸
let firstView = childView.hitTest(childPoint, withEvent: event)
//4.發現不爲空,就回傳
if let notEmpty = firstView {
return notEmpty
}
}
//5.如果沒知道合適的處理事件控件,就返回自己
return self
}
- 上面說的自己寫self.convertPoint(point, toView: childView) 這方法,其實就是下面的算法
func myConvertPoint(point:CGPoint, toView view:UIView) -> CGPoint {
//1.拿到子控件的x,y
let x = view.frame.origin.x
let y = view.frame.origin.y
//2.進行減法,並且返回
return CGPointMake(point.x - x, point.y - y)
}
Objective-C
- 這裏我的另一種思路,但是不提倡
/*只要進入了這個方法,那就代表了這個視圖,是可以與用戶交互,包含那個點的。
但是站在面向對象的角度,思考問題,”誰最清楚,控件能否被點擊,
點在不在自己身上,當然是自己",所以不提倡這樣的寫法*/
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
int count = (int)self.subviews.count;
for( int i = count - 1 ; i >= 0 ; --i ) {
//1.拿到子控件
UIView * childView = self.subviews[i];
//2.轉換座標點
CGPoint childPoint = [self convertPoint:point toView:childView];
//3.判斷子控件能不能與用戶交互
BOOL flagEnable = childView.userInteractionEnabled == YES || childView.hidden == NO || childView.alpha > 0.01;
//4.判斷點在不在子控件上
BOOL flagInside = [childView pointInside:childPoint withEvent:event];
//5.如果都可以,那麼繼續遞歸
if (flagInside && flagEnable) {
UIView *firstView = [childView hitTest:childPoint withEvent:event];
//6.如果有,就回傳
if (firstView) {
return firstView;
}
}
}
//7.沒找到最合適的就返回自己
return self;
}