亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS開發之事件傳遞響應鏈

2020-07-26 03:29:42
字體:
來源:轉載
供稿:網友

當我們在使用微信等工具,點擊掃一掃,就能打開二維碼掃描視圖。在我們點擊屏幕的時候,iphone OS獲取到了用戶進行了“單擊”這一行為,操作系統把包含這些點擊事件的信息包裝成UITouch和UIEvent形式的實例,然后找到當前運行的程序,逐級尋找能夠響應這個事件的對象,直到沒有響應者響應。這一尋找的過程,被稱作事件的響應鏈,如下圖所示,不用的響應者以鏈式的方式尋找

事件響應鏈

一、響應者

在iOS中,能夠響應事件的對象都是UIResponder的子類對象。UIResponder提供了四個用戶點擊的回調方法,分別對應用戶點擊開始、移動、點擊結束以及取消點擊,其中只有在程序強制退出或者來電時,取消點擊事件才會調用。

UIResponder的點擊事件

在自定義UIView為基類的控件時,我們可以重寫這幾個方法來進行點擊回調。在回調中,我們可以看到方法接收兩個參數,一個UITouch對象的集合,還有一個UIEvent對象。這兩個參數分別代表的是點擊對象和事件對象。

1、事件對象
iOS使用UIEvent表示用戶交互的事件對象,在UIEvent.h文件中,我們可以看到有一個UIEventType類型的屬性,這個屬性表示了當前的響應事件類型。分別有多點觸控、搖一搖以及遠程操作(在iOS之后新增了3DTouch事件類型)。在一個用戶點擊事件處理過程中,UIEvent對象是唯一的
2、點擊對象
UITouch表示單個點擊,其類文件中存在枚舉類型UITouchPhase的屬性,用來表示當前點擊的狀態。這些狀態包括點擊開始、移動、停止不動、結束和取消五個狀態。每次點擊發生的時候,點擊對象都放在一個集合中傳入UIResponder的回調方法中,我們通過集合中對象獲取用戶點擊的位置。其中通過- (CGPoint)locationInView:(nullable UIView *)view獲取當前點擊坐標點,- (CGPoint)previousLocationInView:(nullable UIView *)view獲取上個點擊位置的坐標點。
為了確認UIView確實是通過UIResponder的點擊方法響應點擊事件的,我創建了UIView的類別,并重寫+ (void)load方法,使用method_swizzling的方式交換點擊事件的實現

+ (void)load  Method origin = class_getInstanceMethod([UIView class], @selector(touchesBegan:withEvent:));  Method custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesBegan:withEvent:));  method_exchangeImplementations(origin, custom);   origin = class_getInstanceMethod([UIView class], @selector(touchesMoved:withEvent:));  custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesMoved:withEvent:));  method_exchangeImplementations(origin, custom);   origin = class_getInstanceMethod([UIView class], @selector(touchesEnded:withEvent:));  custom = class_getInstanceMethod([UIView class], @selector(lxd_touchesEnded:withEvent:));  method_exchangeImplementations(origin, custom);} - (void)lxd_touchesBegan: (NSSet *)touches withEvent: (UIEvent *)event{  NSLog(@"%@ --- begin", self.class);  [self lxd_touchesBegan: touches withEvent: event];} - (void)lxd_touchesMoved: (NSSet *)touches withEvent: (UIEvent *)event{  NSLog(@"%@ --- move", self.class);  [self lxd_touchesMoved: touches withEvent: event];} - (void)lxd_touchesEnded: (NSSet *)touches withEvent: (UIEvent *)event{  NSLog(@"%@ --- end", self.class);  [self lxd_touchesEnded: touches withEvent: event];}

在新建的項目中,我分別創建了AView、BView、CView和DView四個UIView的子類,然后點擊任意一個位置:

項目結構圖

在我點擊上圖綠色視圖的時候,控制臺輸出了下面的日志(日期部分已經去除):

CView --- beginCView --- end

由此可見在我們點擊UIView的時候,是通過touches相關的點擊事件進行回調處理的。

除了touches回調的幾個點擊事件,手勢UIGestureRecognizer對象也可以附加在view上,來實現其他豐富的手勢事件。在view添加單擊手勢之后,原來的touchesEnded方法就無效了。最開始我一直認為view添加手勢之后,原有的touches系列方法全部無效。但是在測試demo中,發現view添加手勢之后,touchesBegan方法是有進行回調的,但是moved跟ended就沒有進行回調。因此,在系統的touches事件處理中,在touchesBegan之后,應該是存在著一個調度后續事件(nextHandler)處理的方法,個人猜測事件調度的處理大致如下圖示:

事件調度

二、響應鏈傳遞

上面已經介紹了某個控件在接收到點擊事件時的處理,那么系統是怎么通過用戶點擊的位置找到處理點擊事件的view的呢?
在上文我們已經說過了系統通過不斷查找下一個響應者來響應點擊事件,而所有的可交互控件都是UIResponder直接或者間接的子類,那么我們是否可以在這個類的頭文件中找到關鍵的屬性呢?

正好存在著這么一個方法:- (nullable UIResponder *)nextResponder,通過方法名我們不難發現這是獲取當前view的下一個響應者,那么我們重寫touchesBegan方法,逐級獲取下一響應者,直到沒有下一個響應者位置。相關代碼如下:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{  UIResponder * next = [self nextResponder];  NSMutableString * prefix = @"".mutableCopy;   while (next != nil) {    NSLog(@"%@%@", prefix, [next class]);    [prefix appendString: @"--"];    next = [next nextResponder];  }  }

控制臺輸出的所有下級事件響應者如下:

AView--UIView----ViewController------UIWindow--------UIApplication----------AppDelegate

雖然結果非常有層次,但是從系統逐級查找響應者的角度上來說,這個輸出的順序是剛好相反的。為什么會出現這種問題呢?我們可以看到輸出中存在一個ViewController類,說明UIViewController也是UIResponder的子類。但是我們可以發現,controller是一個view的管理者,即便它是響應鏈的成員之一,但是按照邏輯來說,控制器不應該是系統查找對象之一,通過nextResponder方法查找的這個思路是不正確的。

后來,發現在UIView的頭文件中存在這么兩個方法,分別返回UIView和BOOL類型的方法:

- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;  // recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event;  // default returns YES if point is in bounds

根據方法名,一個是根據點擊坐標返回事件是否發生在本視圖以內,另一個方法是返回響應點擊事件的對象。通過這兩個方法,我們可以猜到,系統在收到點擊事件的時候通過不斷遍歷當前視圖上的子視圖的這些方法,獲取下一個響應的視圖。因此,繼續通過method_swizzling方式修改這兩個方法的實現,并且測試輸出如下:

UIStatusBarWindow can answer 1UIStatusBar can answer 0UIStatusBarForegroundView can answer 0UIStatusBarServiceItemView can answer 0UIStatusBarDataNetworkItemView can answer 0UIStatusBarBatteryItemView can answer 0UIStatusBarTimeItemView can answer 0hit view: UIStatusBarhit view: UIStatusBarWindowUIWindow can answer 1UIView can answer 1hit view: _UILayoutGuidehit view: _UILayoutGuideAView can answer 1DView can answer 0hit view: DViewBView can answer 0hit view: BViewhit view: AViewhit view: UIViewhit view: UIWindow...... //下面是touches方法的輸出

最上面的UIStatusBar開頭的類型大家可能沒見過,但是不妨礙我們猜到這是狀態欄相關的一些視圖,具體可以查找蘋果的文檔中心(Xcode中快捷鍵shift+command+0打開)。從輸出中不難看出系統先調用pointInSide: WithEvent:判斷當前視圖以及這些視圖的子視圖是否能接收這次點擊事件,然后在調用hitTest: withEvent:依次獲取處理這個事件的所有視圖對象,在獲取所有的可處理事件對象后,開始調用這些對象的touches回調方法

通過輸出的方法調用,我們可以看到響應查找的順序是: UIStatusBar相關的視圖 -> UIWindow -> UIView -> AView -> DView -> BView(系統在事件鏈傳遞的過程中一定會遍歷所有的子視圖判斷是否能夠響應點擊事件),以本文demo為例,我們可以得出事件響應鏈查找的圖示如下:

響應者查找流程

那么在上面的查找響應者流程完成之后,系統會將本次事件中的點擊轉換成UITouch對象,然后將這些對象和UIEvent類型的事件對象傳遞給touchesBegan方法,you

不僅如此,從上面輸出的nextResponder來看,所有的響應者都是在查找中返回可響應點擊的視圖。因此,我們可以推測出UIApplication對象維護著自己的一個響應者棧,當pointInSide: withEvent:返回yes的時候,響應者入棧。

響應者棧

棧頂的響應者作為最優先處理事件的對象,假設AView不處理事件,那么出棧,移交給UIView,以此下去,直到事件得到了處理或者到達AppDelegate后依舊未響應,事件被摒棄為止。通過這個機制我們也可以看到controller是響應者棧中的例外,即便沒有pointInSide: withEvent:的方法返回可響應,controller依舊能夠入棧成為UIView的下一個響應者。

、響應鏈應用

既然已經知道了系統是怎么獲取響應視圖的流程了,那么我們可以通過重寫查找事件處理者的方法來實現不規則形狀點擊。最常見的不規則視圖就是圓形視圖,在demo中我設置view的寬高為200,那么重寫方法事件如下:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event{  const CGFloat halfWidth = 100;  CGFloat xOffset = point.x - 100;  CGFloat yOffset = point.y - 100;  CGFloat radius = sqrt(xOffset * xOffset + yOffset * yOffset);  return radius <= halfWidth;}

最終的效果圖如下:

以上就是本文的全部內容,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩av网站在线| 91精品国产电影| 亚洲午夜女主播在线直播| 尤物九九久久国产精品的分类| 亚洲自拍欧美另类| 欧美日韩免费区域视频在线观看| 欧美中文在线观看国产| 日韩成人高清在线| 中文日韩电影网站| 国产欧美精品日韩精品| 国产成人高清激情视频在线观看| 精品久久久久久久久久久久| 欧美在线观看www| 久久久久久久999| 国产香蕉精品视频一区二区三区| 亚洲男人天堂视频| 久久99久久久久久久噜噜| 欧美午夜激情在线| 日韩视频第一页| 亚洲成人xxx| 国产精品极品美女在线观看免费| 麻豆一区二区在线观看| 青草青草久热精品视频在线网站| 国产精品一区二区三| 精品久久久久久国产91| 国产91在线播放精品91| 日韩av大片在线| 欧美精品aaa| 亚洲美女精品成人在线视频| 久久夜色精品亚洲噜噜国产mv| 欧美肥老太性生活视频| 亚洲国产精品va| 欧美精品在线播放| 国产亚洲一级高清| 亚洲国产高清福利视频| 欧美日韩亚洲视频一区| 精品无人区太爽高潮在线播放| 亚洲欧美日韩在线一区| 5278欧美一区二区三区| 欧美亚洲国产精品| 亚洲电影免费观看高清完整版在线| 国产精品久久不能| 97成人精品视频在线观看| 欧美成人激情视频免费观看| 日韩欧美在线免费观看| 九九视频这里只有精品| 欧美成人免费网| 激情久久av一区av二区av三区| 亚洲精品福利视频| 黑人与娇小精品av专区| 国产一区二区三区在线免费观看| 亚洲精品aⅴ中文字幕乱码| 欧美午夜片在线免费观看| 亚洲一区二区在线| 国产视频福利一区| 136fldh精品导航福利| 国产精品亚洲一区二区三区| 正在播放欧美视频| 国产精品久久久久久影视| 日本免费在线精品| 成人福利免费观看| www高清在线视频日韩欧美| 欧美巨乳在线观看| 久久久亚洲国产| 在线视频日韩精品| 国产91成人video| 日韩中文字幕网| 亚洲色图第一页| 亚洲男人天堂2024| 日韩激情av在线播放| 亚洲成人av资源网| 最近中文字幕mv在线一区二区三区四区| 欧美一区二区三区精品电影| 欧美韩国理论所午夜片917电影| 日韩高清中文字幕| 精品亚洲国产视频| 91精品国产高清自在线| 久久亚洲欧美日韩精品专区| 精品国内产的精品视频在线观看| 国产精品亚洲片夜色在线| 免费成人高清视频| xxxxxxxxx欧美| 国产成人在线一区二区| 国产欧美精品va在线观看| 亚洲色图国产精品| 久久九九有精品国产23| 8x拔播拔播x8国产精品| 久久久久久久一区二区三区| 国产精品揄拍500视频| 午夜精品一区二区三区在线| 日产精品久久久一区二区福利| 97超碰色婷婷| 91精品国产91久久久久久久久| 永久免费毛片在线播放不卡| 国产精品久久久久久久av电影| 欧亚精品中文字幕| 欧美人成在线视频| 一区国产精品视频| 国产欧美日韩免费看aⅴ视频| 欧美最猛性xxxxx亚洲精品| 成年无码av片在线| 亚洲第一视频网站| 国模精品系列视频| 精品亚洲男同gayvideo网站| 亚洲国产日韩精品在线| 日韩中文字幕网址| 久久人人爽亚洲精品天堂| 国内精品模特av私拍在线观看| 久久中国妇女中文字幕| 久久精品成人欧美大片古装| 91免费观看网站| 性欧美xxxx视频在线观看| 国产成人综合一区二区三区| 岛国av一区二区在线在线观看| 亚洲国产成人久久| 欧美丰满片xxx777| 色无极影院亚洲| 国产精品入口免费视| 欧美在线视频免费观看| 成人性生交大片免费观看嘿嘿视频| 日韩精品亚洲元码| 国产精品高清免费在线观看| 亚洲色图综合久久| 国产精品久久久久久久9999| 97av在线影院| 日韩在线视频播放| 亚洲va久久久噜噜噜| 国产精品久久一区主播| 日韩免费观看高清| 一级做a爰片久久毛片美女图片| 亚洲欧美激情精品一区二区| 欧美老女人在线视频| 国产欧美日韩精品丝袜高跟鞋| 欧美视频在线视频| 久久久天堂国产精品女人| 日韩电影在线观看中文字幕| 黑人巨大精品欧美一区二区| 一个人看的www欧美| 深夜精品寂寞黄网站在线观看| 国产精品v片在线观看不卡| 日本中文字幕久久看| 国产精品99久久久久久久久| 黑人精品xxx一区| 亚洲一区第一页| 欧美性黄网官网| 91美女片黄在线观看游戏| 亚洲风情亚aⅴ在线发布| 久久亚洲精品国产亚洲老地址| 日韩欧美在线视频观看| 国产精品自产拍在线观看中文| 国产精品白嫩初高中害羞小美女| 亚洲精品suv精品一区二区| 成人av在线网址| 亚洲精品在线91| 国产精品∨欧美精品v日韩精品| 亚洲国产另类久久精品| 日韩在线观看电影| 精品美女久久久久久免费| 久久久久久久电影一区| 疯狂做受xxxx欧美肥白少妇| 久久久电影免费观看完整版| 日韩经典中文字幕在线观看| 中日韩美女免费视频网址在线观看| 国产91色在线||