概述
iOS中事件有觸摸事件睁冬、加速計事件召锈、遠程控制事件,下面以觸摸事件為例研究下iOS事件相關(guān)的內(nèi)容
UIResponsder
iOS中繼承了UIResponsder
的對象才能夠響應(yīng)事件,如UIViewController
描函、UIView
今膊、UIApplication
些阅。
UIResponsder能夠響應(yīng)事件的原因是:提供了幾個方法來處理事件
//觸摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;//手指觸摸事件開始時調(diào)用該方法
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;//手指在屏幕上移動時調(diào)用該方法
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
//加速計事件
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event;
- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event;
//遠程控制事件
- (void)remoteControlReceivedWithEvent:(UIEvent *)event;
備注:
- 當多個手指同時觸摸屏幕時,只會調(diào)用一次
touchesBegan:withEvent:
方法斑唬,touches
中包含多個UITouch
對象市埋,分別對應(yīng)著觸摸屏幕的手指 - 如果是
UIViewController
的事件昌跌,在UIViewController
中重寫上面用到的方法即可件炉;如果是UIView
的事件,需要在子類中重寫上面用到的方法
實例:觸摸屏幕篡撵,移動屏幕上的某個UIButton
在UIView
的子類中重寫touchesMoved: withEvent:
方法
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [touch locationInView:self];//當前點的位置
CGPoint previousPoint = [touch previousLocationInView:self];//上一個點的位置
CGFloat xPoint = point.x - previousPoint.x;
CGFloat yPoint = point.y - previousPoint.y;
NSLog(@"point:%@;previousPoint:%@;xPoint:%f;yPoint:%f",NSStringFromCGPoint(point), NSStringFromCGPoint(previousPoint), xPoint, yPoint);
CGPoint centerPoint = self.btn.center;
centerPoint = CGPointMake(centerPoint.x + xPoint, centerPoint.y + yPoint);
self.btn.center = centerPoint;
}
iOS 事件處理過程
iOS事件分為事件傳遞褐着、事件響應(yīng)兩個過程坷澡。
事件的產(chǎn)生
發(fā)生觸摸事件后,系統(tǒng)會將事件加入到由UIApplication管理的隊列(先進先出FIFO)献起,然后將隊列最前面的事件分發(fā)下去:UIApplication->keyWindow->視圖層次結(jié)構(gòu)洋访,找到合適的視圖控件后,就會調(diào)用對應(yīng)控件的事件方法(touchesBegan: withEvent:
等)來處理對應(yīng)的事件谴餐。
事件的傳遞
觸摸事件的傳遞是從父控件傳遞到子控件
- 尋找響應(yīng)事件的控件步驟
- 首先判斷keyWindow是否能響應(yīng)事件
- 觸摸點是否在自己身上
- 從后往前遍歷子控件姻政,重復(fù)前面的兩個步驟(首先查找數(shù)組中最后一個元素)
- 如果沒有符合條件的子控件,那就認為自己可以處理
- UIView不能響應(yīng)事件的三種情況
- 不允許交互userInteractionEnabled = NO
- 隱藏的控件不能接收事件
- 透明度:如果設(shè)置一個控件的透明度<0.01岂嗓,會直接影響子控件的透明度汁展。
事件傳遞實例
注意:如果父控件不能接收觸摸事件,那么子控件就不可能接收到觸摸事件
查找符合條件的控件原理
主要是通過以下兩個控件實現(xiàn)查找
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;//返回nil厌殉,表明當前view以及子view沒有能夠響應(yīng)事件的;如果不為nil食绿,則表明當前view中有能響應(yīng)事件的
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;//返回YES,表明觸摸點在當前view中
通過調(diào)用hitTest:withEvent:
可以判斷是否有能夠響應(yīng)事件的控件公罕,具體步驟為:
產(chǎn)生觸摸事件->UIApplication事件隊列->[UIWindow hitTest:withEvent:]->返回合適的view->[子控件 hitTest:withEvent:]->返回合適的view
不過子控件是否能夠響應(yīng)事件器紧,父控件都會把事件傳遞給子控件,如果子控件不能響應(yīng)事件楼眷,則父控件響應(yīng)事件
實例:view中的button響應(yīng)事件
參考iOS超出父視圖后點擊事件不響應(yīng)铲汪,文中想要view中的button響應(yīng)點擊事件,因此重寫了hitTest:withEvent:
方法
響應(yīng)者鏈
在iOS程序中無論是最后面的UIWindow還是最前面的某個按鈕罐柳,它們的擺放是有前后關(guān)系的掌腰,一個控件可以放到另一個控件上面或下面,那么用戶點擊某個控件時是觸發(fā)上面的控件還是下面的控件呢张吉,這種先后關(guān)系構(gòu)成一個鏈條就叫“響應(yīng)者鏈”齿梁。
事件響應(yīng)過程
- 如果當前控件無法響應(yīng)事件,就將事件按照視圖層級結(jié)構(gòu)(最上層是根視圖)向上傳遞。如果當前view是控制器的view勺择,那么控制器就是上一個響應(yīng)者创南,事件就傳遞給控制器;如果當前view不是控制器的view酵幕,那么父視圖就是當前view的上一個響應(yīng)者扰藕,事件就傳遞給它的父視圖
- 在視圖層次結(jié)構(gòu)的最頂級視圖,如果也不能處理收到的事件或消息芳撒,則其將事件或消息傳遞給window對象進行處理
- 如果window對象也不處理邓深,則其將事件或消息傳遞給UIApplication對象
- 如果UIApplication也不能處理該事件或消息,則將其丟棄
總結(jié)
- 觸摸屏幕產(chǎn)生觸摸事件后笔刹,觸摸事件會被添加到由UIApplication管理的事件隊列中
- UIApplication會從事件隊列中取出最前面的事件芥备,把事件傳遞給keyWindow
- keyWindow會在視圖層次結(jié)構(gòu)中找到一個最合適的視圖來處理觸摸事件
- 最合適的view會調(diào)用自己的touches方法處理事件,如果不能處理事件舌菜,就將事件沿著響應(yīng)者鏈向上拋
問題與解決方案
- 向 WKWebView 添加 UITapGestureRecognizer 點擊識別萌壳,會發(fā)現(xiàn)不起作用,怎樣解決日月?
解決:實現(xiàn) gesture 的 delegate袱瓮,允許兩個 gesture 同時識別
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}