《Event Handling Guide for iOS》筆記


用戶可以以觸摸屏幕或搖動設備等多種方式操作他們的iOS設備妻味。 iOS系統(tǒng)會對用戶操作設備的時動作做出解釋并把該信息傳遞給應用程序。應用程序?qū)τ脩舾鞣N自然的動作都能處理越全面欣福,用戶體驗也就越好责球。
iOS系統(tǒng)通過事件(Event)將用戶的操作傳遞給app,事件分為多種形式:多觸摸事件拓劝,運動事件以及控制多媒體(通過外部硬件控制)的事件雏逾。程序中可以通過內(nèi)置在UIKit里的常用手勢和一些定制化手勢來響應用戶的各種操作手勢。如果可能的話郑临,最好是使用這些內(nèi)置的手勢栖博,可以縮減了代碼總量。內(nèi)置在UIKit里標準控件的手勢例如UIButtonUISlider牧抵, 分別可以對點擊和拖動手勢響應笛匙。如果想應用程序識別一個獨特的手勢侨把,比如一個漩渦狀運動,可以創(chuàng)建自定義手勢妹孙。 手勢識別器為復雜的事件處理邏輯提供了一個高層次的抽象秋柄,應用程序中實現(xiàn)觸摸事件處理(touch-event handling)應該考慮使用。

UIKit 內(nèi)置的手勢及對應class包括:

點擊Tapping (any number of taps)::UITapGestureRecognizer
向內(nèi)/外捏Pinching in and out (for zooming a view)::UIPinchGestureRecognizer
滑動/拖動Panning or dragging::UIPanGestureRecognizer
快速滑動Swiping (in any direction)::UISwipeGestureRecognizer
旋轉(zhuǎn)Rotating (fingers moving in opposite directions)::UIRotationGestureRecognizer
長按Long press (also known as “touch and hold”)::UILongPressGestureRecognizer

手勢可以分為離散手勢和連續(xù)手勢蠢正。 一個離散手勢骇笔,如一次tap或一次swipe,只發(fā)生一次嚣崭。一個連續(xù)手勢笨触,如捏合,發(fā)生時持續(xù)一段時間雹舀。 對于離散手勢芦劣,手勢識別就給目標發(fā)送一個操作消息。對于連續(xù)手勢说榆,手勢識別則一直發(fā)送操作消息給目標直到操作結(jié)束虚吟。


離散手勢與連續(xù)手勢

你需要做三件事來添加一個內(nèi)建手勢識別到應用程序:
創(chuàng)建并配置一個手勢識別實例。該步驟包括分配一個響應手勢的目標签财,操作串慰,以及手勢指定的各種屬性(比如 numberOfTapsRequired).
把手勢識別連接到一個視圖。
實現(xiàn)響應手勢的操作方法唱蒸。

對于離散型手勢:

- (void)viewDidLoad
 {
     [super viewDidLoad];
 
     // Create and initialize a tap gesture
     UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
          initWithTarget:self action:@selector(respondToTapGesture:)];
 
     // Specify that the gesture must be a single tap
     tapRecognizer.numberOfTapsRequired = 1;
 
     // Add the tap gesture recognizer to the view
     [self.view addGestureRecognizer:tapRecognizer];
 }

// 手勢的響應方法
- (IBAction)showGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer 
{
       // Get the location of the gesture
      CGPoint location = [recognizer locationInView:self.view];
 
       // Display an image view at that location
      [self drawImageForGestureRecognizer:recognizer atPoint:location];
 
       // Animate the image view so that it fades out
      [UIView animateWithDuration:0.5 animations:^{
           self.imageView.alpha = 0.0;
      }];
}```
###對于連續(xù)型手勢:

// Respond to a rotation gesture

  • (IBAction)showGestureForRotationRecognizer:(UIRotationGestureRecognizer *)recognizer
    {
    CGPoint location = [recognizer locationInView:self.view];

     // 隨著手勢旋轉(zhuǎn)圖片
     CGAffineTransform transform = CGAffineTransformMakeRotation([recognizer rotation]);
     self.imageView.transform = transform;
    
     [self drawImageForGestureRecognizer:recognizer atPoint:location];
    
    // 通過手勢狀態(tài)決定最終形式
    if (([recognizer state] == UIGestureRecognizerStateEnded) || ([recognizer state] == UIGestureRecognizerStateCancelled)) {
         [UIView animateWithDuration:0.5 animations:^{
              self.imageView.alpha = 0.0;
              self.imageView.transform = CGAffineTransformIdentity;
         }];
    }
    

}

###手勢狀態(tài)
手勢識別在有限狀態(tài)機中的各種預定的狀態(tài)之間切換邦鲫。所有的手勢識別起點都是“可能的狀態(tài)UIGestureRecognizerStatePossible”

* 對于離散型手勢,手勢可能識別失敗UIGestureRecognizerStateFailed或被識別UIGestureRecognizerStateRecognized神汹,從而結(jié)束識別過程庆捺;

![](http://upload-images.jianshu.io/upload_images/3494096-142c1938d62b0fb1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/480)

* 對于連續(xù)性手勢,每次手勢識別改變狀態(tài)時慎冤,除非過渡到失敗或取消狀態(tài)疼燥,都給它的響應目標發(fā)送消息。當手勢第一次被識別時蚁堤,從可能狀態(tài)UIGestureRecognizerStatePossible轉(zhuǎn)到開始狀態(tài)UIGestureRecognizerStateBegan醉者,然后過渡到改變狀態(tài)UIGestureRecognizerStateChanged, 當手勢發(fā)生時又從改變狀態(tài)變?yōu)楦淖儬顟B(tài)。 當用戶的最后一個手指從視圖上抬起時披诗,手勢識別過渡到結(jié)束狀態(tài)UIGestureRecognizerStateEnded, 識別完成(結(jié)束狀態(tài)是識別完成狀態(tài)的一個別名)撬即。如果一個連續(xù)手勢不再符合某個手勢預期的模式時,識別將過渡到取消狀態(tài)UIGestureRecognizerStateCancelled呈队。

![](http://upload-images.jianshu.io/upload_images/3494096-9a80687726df5df8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/520)


###多個手勢的競爭處理
使用UIGestureRecognizer類方法剥槐,委托方法,以及其子類重寫的方法可以處理如兩個手勢誰先響應宪摧、是否同時粒竖、哪個手勢禁止響應某個動作等競爭問題颅崩。最常見的容易沖突的swipe和pan手勢,swipe手勢會首先被識別為pan手勢蕊苗,但是如何打破這個順序沿后,讓swipe首先被識別,如果確定不是swipe再識別pan朽砰?
  • (void)viewDidLoad
    {
    [super viewDidLoad];
    [self.panRecognizer requireGestureRecognizerToFail:self.swipeRecognizer];
    }
requireGestureRecognizerToFail: 使pan手勢在等待swipe手勢識別過渡到Failed狀態(tài)期間尖滚,始終處于Possible狀態(tài)。如果swipe手勢識別失敗了瞧柔,pan手勢分析事件并變?yōu)橄聜€狀態(tài)漆弄。如果swipe手勢識別過渡到Recognized 或者 Began狀態(tài),pan手勢就變?yōu)镕ailed 狀態(tài)造锅。 關(guān)于狀態(tài)過渡的信息參考[“Gesture Recognizers Operate in a Finite State Machine.”](https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/GestureRecognizer_basics/GestureRecognizer_basics.html#//apple_ref/doc/uid/TP40009541-CH2-SW22)

UIGestureRecognizerDelegate協(xié)議提供了方法來阻止手勢識別分析觸摸撼唾。 可以使用協(xié)議中的
 gestureRecognizer:shouldReceiveTouch:方法 或  gestureRecognizerShouldBegin: (如果視圖或視圖控制器不能成為手勢識別的代理,可以使用)

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ([touch view] == self.customSubview)
{
// If it is, prevent all of the delegate's gesture recognizers from receiving the touch
return NO;
}
return YES;
}

兩個手勢識別不能同時識別它們的不同手勢备绽,通過代理方法可以讓兩個手勢識別同時識別一個手勢gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: 
如果你想要控制兩個識別的交互為一個單向關(guān)系券坞,你可以重寫canPreventGestureRecognizer:方法并返回NO(默認為YES)。 比如肺素,如果你想用一個旋轉(zhuǎn)手勢阻止一個捏合手勢,但是又不想捏合手勢阻止一個旋轉(zhuǎn)手勢宇驾,可以用 rotationGestureRecognizer canPreventGestureRecognizer:pinchGestureRecognizer;
在iOS 6.0 以后版本中倍靡,默認控件操作方法阻止重復手勢識別的行為。如一個按鈕的默認操作是一個單擊课舍。如果你有一個單擊手勢識別綁定到一個按鈕的父視圖上塌西,然后用戶點擊該按鈕,最后按鈕的操作方法接收觸摸事件而不是定制添加的手勢識別筝尾。 還有:
* 單個手勢在 UISlider上的快速滑動捡需,輕掃方向跟slider平行; 
* 單個手指的在 UISwitch 控件上的慢速拖動手勢,方向跟switch平行筹淫。


###觸摸UITouch 和 事件UIEvent
一個觸摸UITouch 是一個手指在屏幕上的存在或運動站辉。 一個手勢有一個或多個觸摸。
一個事件UIEvent包含一個多點觸摸序列的所有觸摸,包含了應用程序決定如何響應該事件的事件信息损姜。 一個多點觸摸序列以一個手指觸摸屏幕開始饰剥,以最后一個手指離開屏幕結(jié)束。 當一個手指移動時摧阅,iOS給事件UIEvent發(fā)送多個觸摸對象UITouch汰蓉。
每個觸摸對象只跟蹤一個手指,并且只在觸摸序列期間跟蹤棒卷。 在序列期間顾孽,UIKit跟蹤手指并更新觸摸對象的各種屬性祝钢。 這些屬性包括觸摸階段,它在視圖中的位置若厚,前一個位置拦英,以及時間戳。
觸摸階段表明一個觸摸何時開始盹沈,它是移動的還是靜止的龄章,以及它何時結(jié)束---當手指不再觸摸屏幕的時間。應用程序在任何觸摸的每個階段之間接收事件對象乞封。

![](http://upload-images.jianshu.io/upload_images/3494096-0ed90934e7b493fd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/800)
各個階段對應代理方法:
touchesBegan:withEvent: 當一個或多個手指觸摸屏幕時調(diào)用
touchesMoved:withEvent:當一個或多個手勢移動時調(diào)用
touchesEnded:withEvent:當一個或多個手指離開屏幕時調(diào)用
touchesCancelled:withEvent:當觸摸序列被系統(tǒng)事件取消時調(diào)用做裙,比如有一個來電。
注意: 這些方法跟手勢識別狀態(tài)沒有關(guān)聯(lián)



當一個觸摸發(fā)生時肃晚,觸摸對象從UIApplication對象傳遞到UIWindow對象锚贱。 然后,窗口首先把觸摸發(fā)送給觸摸發(fā)生的視圖上關(guān)聯(lián)的手勢識別器关串,如果識別出響應手勢將響應者變?yōu)橐晥D所在vc拧廊,而不再發(fā)給視圖。如果沒有識別出手勢晋修,才會發(fā)送給視圖對象自身吧碾。即下圖123的順序:

![](http://upload-images.jianshu.io/upload_images/3494096-a5637ceebb8d1521.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/520)
我們可以通過屬性delaysTouchesBegan等延遲將事件傳遞給視圖,而讓手勢識別先進行完墓卦。
當你通過子類化實現(xiàn)一個定制的手勢識別器時倦春,最重要的事情是正確地使用手勢識別器的state,在各個狀態(tài)下獲取手勢的情況來識別落剪。
- (void)reset; // 在Recognized/Ended, Canceled, 或者Failed狀態(tài)時復位
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;// 離散手勢無此狀態(tài)
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;

###響應鏈
當iOS系統(tǒng)識別到一個事件睁本,它把事件傳遞給看起來最有可能處理的初始對象,如果初始對象不能處理該事件忠怖,iOS繼續(xù)把它傳遞給更多的對象呢堰,直到找到能夠處理該事件的對象。這些有順序的對象構(gòu)成一個響應鏈(responder chain) 凡泣。
![響應鏈](http://upload-images.jianshu.io/upload_images/3494096-87ede27a9574ebe3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/600)
響應鏈與事件的傳遞鏈是互逆的:
事件傳遞過程:
iOS設備的硬件檢測到用戶的觸摸事件 -> 產(chǎn)生UIEvent -> UIApplication -> UIWindow -> ......


![](http://upload-images.jianshu.io/upload_images/3494096-61b5a96cc1aab35b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/840)
iOS系統(tǒng)通過hittest尋找響應者的過程就是一個遍歷樹的子節(jié)點的過程枉疼,需要遍歷所有相關(guān)的vc和vc上面的所有view及其子view,直到最葉子節(jié)點的view響應问麸,如果沒有子view能響應往衷,就找到最葉的vc;如果沒有葉子vc可以響應严卖,就找到UIWindow席舍;否則找到UIApplication。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哮笆,一起剝皮案震驚了整個濱河市来颤,隨后出現(xiàn)的幾起案子汰扭,更是在濱河造成了極大的恐慌,老刑警劉巖福铅,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萝毛,死亡現(xiàn)場離奇詭異,居然都是意外死亡滑黔,警方通過查閱死者的電腦和手機笆包,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來略荡,“玉大人庵佣,你說我怎么就攤上這事⊙炊担” “怎么了巴粪?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粥谬。 經(jīng)常有香客問我肛根,道長,這世上最難降的妖魔是什么漏策? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任派哲,我火速辦了婚禮,結(jié)果婚禮上掺喻,老公的妹妹穿的比我還像新娘狮辽。我一直安慰自己,他們只是感情好巢寡,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著椰苟,像睡著了一般抑月。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上舆蝴,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天谦絮,我揣著相機與錄音,去河邊找鬼洁仗。 笑死层皱,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的赠潦。 我是一名探鬼主播叫胖,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼她奥!你這毒婦竟也來了瓮增?” 一聲冷哼從身側(cè)響起怎棱,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绷跑,沒想到半個月后拳恋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡砸捏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年谬运,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垦藏。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡梆暖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出膝藕,到底是詐尸還是另有隱情式廷,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布芭挽,位于F島的核電站滑废,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袜爪。R本人自食惡果不足惜蠕趁,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辛馆。 院中可真熱鬧俺陋,春花似錦、人聲如沸昙篙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苔可。三九已至缴挖,卻和暖如春狡逢,著一層夾襖步出監(jiān)牢的瞬間炮车,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工袱讹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留同蜻,地道東北人棚点。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像湾蔓,于是被迫代替她去往敵國和親瘫析。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

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

  • 好奇觸摸事件是如何從屏幕轉(zhuǎn)移到APP內(nèi)的?困惑于Cell怎么突然不能點擊了颁股?糾結(jié)于如何實現(xiàn)這個奇葩響應需求么库?亦或是...
    Lotheve閱讀 56,666評論 51 597
  • 在iOS開發(fā)中經(jīng)常會涉及到觸摸事件。本想自己總結(jié)一下甘有,但是遇到了這篇文章诉儒,感覺總結(jié)的已經(jīng)很到位,特此轉(zhuǎn)載亏掀。作者:L...
    WQ_UESTC閱讀 5,989評論 4 26
  • 手勢識別器是附加到視圖的對象忱反,將低級別事件處理代碼轉(zhuǎn)換為更高級別的操作,它允許視圖以控件執(zhí)行的方式響應操作滤愕。 手勢...
    坤坤同學閱讀 4,063評論 0 9
  • -- iOS事件全面解析 概覽 iPhone的成功很大一部分得益于它多點觸摸的強大功能巩割,喬布斯讓人們認識到手機其實...
    翹楚iOS9閱讀 2,942評論 0 13
  • 萬物皆為靈宣谈,人是靈性的,但又架馭主宰世間所有事物的键科,這是被我們普遍熟知的闻丑。可我們有了靈性勋颖,又同樣被賦予了欲望嗦嗡,當一...
    離奇之語閱讀 493評論 0 1