iOS事件響應(yīng)鏈

前言

當(dāng)用戶點(diǎn)擊付款跳轉(zhuǎn)到付款界面、點(diǎn)擊掃一掃進(jìn)入掃描二維碼視圖缚态。
當(dāng)我們點(diǎn)擊屏幕的時(shí)候喊儡,這個(gè)點(diǎn)擊事件由硬件層傳向iPhone OS(操作系統(tǒng)),
然后操作系統(tǒng)把這些信息包裝成UITouch(點(diǎn)擊對象)和UIEvent(事件對象)哩治,然后找到當(dāng)前運(yùn)行的程序秃踩,
逐級尋找能夠響應(yīng)這次事件的響應(yīng)者,這一尋找過程稱為事件的響應(yīng)鏈业筏。
過程:AppDelegate -> UIApplication -> UIWindow ->UIController(UIResponder子類)-UIView->subViews

  • UITouch
    UITouch表示單個(gè)點(diǎn)擊憔杨,其類文件中存在枚舉類型UITouchPhase的屬性,用來表示當(dāng)前點(diǎn)擊的狀態(tài)蒜胖。
    這些狀態(tài)包括點(diǎn)擊開始消别、移動(dòng)、停止不動(dòng)台谢、結(jié)束和取消五個(gè)狀態(tài)寻狂。
    每次點(diǎn)擊發(fā)生的時(shí)候,點(diǎn)擊對象都放在一個(gè)集合中傳入UIResponder的回調(diào)方法中朋沮。
    我們通過集合中對象獲取用戶點(diǎn)擊的位置蛇券,其中通過- (CGPoint)locationInView:(nullable UIView *)view獲取當(dāng)前點(diǎn)擊坐標(biāo)點(diǎn),
    - (CGPoint)previousLocationInView:(nullable UIView *)view獲取上個(gè)點(diǎn)擊位置的坐標(biāo)點(diǎn)樊拓。
  • UIEvent
    iOS使用UIEvent表示用戶交互的事件對象纠亚,在UIEvent.h文件中,我們可以看到有一個(gè)UIEventType類型的屬性;
    這個(gè)屬性表示了當(dāng)前的響應(yīng)事件類型;分別有多點(diǎn)觸控筋夏、搖一搖以及新增的3DTouch事件類型蒂胞。
    在一個(gè)用戶點(diǎn)擊事件處理過程中,UIEvent對象是唯一的条篷。
查找和響應(yīng)流程.jpg

Hit-test View

決定誰是Hit-test View通過不斷遞歸- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event以下兩個(gè)函數(shù)實(shí)現(xiàn)的:

// recursively calls -pointInside:withEvent:. point is in the receiver's coordinate system
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;

// default returns YES if point is in bounds
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

Hit-test檢查機(jī)制

  • 通過調(diào)用自身- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event如果返回YES骗随,則繼續(xù)遍歷View中subViews直到?jīng)]有subViews為止蛤织。
  • 如果當(dāng)前View.userInteractionEnabled = NO,enabled=NO(UIControl),或者alpha<=0.01, View Hidden等情況的時(shí)候,hitTest就不會(huì)調(diào)用自己的- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event了鸿染,直接返回nil钳垮,然后系統(tǒng)去遍歷其他子節(jié)點(diǎn)踏枣。
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
   if (self.alpha <= 0.01 || !self.userInteractionEnabled || self.hidden) { 
      return nil; 
   } 
   BOOL inside = [self pointInside:point withEvent:event]; 
   UIView *hitView = nil; 
   if (inside) { 
      NSEnumerator *enumerator = [self.subviews reverseObjectEnumerator]; 
      for (UIView *subview in enumerator) { 
          hitView = [subview hitTest:point withEvent:event]; 
          if (hitView) { 
              break; 
          } 
      } 
      if (!hitView) { 
        hitView = self; 
      } 
      return hitView; 
  }else{ 
      return nil; 
  }
}

事件分發(fā)處理

  • 當(dāng)找到Hit-test View之后,事件分發(fā)正式開始,如果Hit-test View能處理就直接處理逻族,如果不能處理温技,就交給 The Responder Chain或者
    GestureRecognizer處理茁彭。
  • 獲取可處理事件對象后調(diào)用這些對象touches回調(diào)方法国瓮。
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
  • 如果是UIGestureRecognizer事件,事件分發(fā)又不一樣播瞳,經(jīng)過- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event后走的是UIGestureRecognizerDelegate回調(diào)掸刊。
  • 當(dāng)沒有響應(yīng)者,事件到AppDelegate后依舊未能響應(yīng)赢乓,系統(tǒng)會(huì)放棄此次事件忧侧。

實(shí)際運(yùn)用

  • 為某個(gè)button擴(kuò)大點(diǎn)擊區(qū)域,即使不在button區(qū)域也能響應(yīng)button事件牌芋。
    重載UIButton的-(BOOL)pointInside: withEvent:方法蚓炬,讓Point即使落在Button的Frame外圍也返回YES。
//in custom button .m
//overide this method
- (BOOL)pointInside:(CGPoint)point withEvent:(nullable UIEvent *)event {
    return CGRectContainsPoint(HitTestingBounds(self.bounds, self.minimumHitTestWidth, self.minimumHitTestHeight), point);
}

CGRect HitTestingBounds(CGRect bounds, CGFloat minimumHitTestWidth, CGFloat minimumHitTestHeight) {
    CGRect hitTestingBounds = bounds;
    if (minimumHitTestWidth > bounds.size.width) {
        hitTestingBounds.size.width = minimumHitTestWidth;
        hitTestingBounds.origin.x -= (hitTestingBounds.size.width - bounds.size.width)/2;
    }
    if (minimumHitTestHeight > bounds.size.height) {
        hitTestingBounds.size.height = minimumHitTestHeight;
        hitTestingBounds.origin.y -= (hitTestingBounds.size.height - bounds.size.height)/2;
    }
    return hitTestingBounds;
}
  • 不規(guī)則形狀點(diǎn)擊事件處理躺屁。
    重寫-(BOOL)pointInside: withEvent:touches回調(diào)處理

  • UIResponder響應(yīng)鏈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肯夏,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子犀暑,更是在濱河造成了極大的恐慌驯击,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耐亏,死亡現(xiàn)場離奇詭異徊都,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)广辰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門暇矫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人择吊,你說我怎么就攤上這事袱耽。” “怎么了干发?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長史翘。 經(jīng)常有香客問我枉长,道長冀续,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任必峰,我火速辦了婚禮洪唐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吼蚁。我一直安慰自己凭需,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布肝匆。 她就那樣靜靜地躺著粒蜈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪旗国。 梳的紋絲不亂的頭發(fā)上枯怖,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機(jī)與錄音能曾,去河邊找鬼度硝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛寿冕,可吹牛的內(nèi)容都是我干的蕊程。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼驼唱,長吁一口氣:“原來是場噩夢啊……” “哼藻茂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起曙蒸,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤捌治,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后纽窟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肖油,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年臂港,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了森枪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡审孽,死狀恐怖县袱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情佑力,我是刑警寧澤式散,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站打颤,受9級特大地震影響暴拄,放射性物質(zhì)發(fā)生泄漏漓滔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一乖篷、第九天 我趴在偏房一處隱蔽的房頂上張望响驴。 院中可真熱鬧,春花似錦撕蔼、人聲如沸豁鲤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽琳骡。三九已至,卻和暖如春诉探,著一層夾襖步出監(jiān)牢的瞬間日熬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工肾胯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留竖席,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓敬肚,卻偏偏與公主長得像毕荐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子艳馒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容