babyfish0226 ![]() |
原文連結:Handling Multi-Touch Events 翻譯作者:adammisko 文章校稿:尚未校稿 處理多點觸控事件 Handling Multi-Touch Events 處理多點觸控事件,你自己的定制UIview子類別(或者,不頻常,你自己的定制UIApplication或UIwindow 子類別),要實行至少在其中的UIResponder方法事件處理。以下章節描述這些方法,討論的方法處理常用手勢,顯示出典型的 回應者物件處理一個複雜序列多點觸控事件,並提出了一些技術事件處理。 在這一章節: 事件的處理方法 事件的處理方法 ﹣(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; ﹣(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; ﹣(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; 應用程式發送這些訊息,針對某個觸摸(touches) 階段,當有新的或改變觸摸: . 一個或更多的手指觸摸下在螢幕上,它發送了 touchesBegan : withEvent :訊息。 . 一個或更多的手指移動 ,它發送了touchesMoved : withEvent :訊息 。 . 一個或更多的手指 從螢幕上離開 ,它發送了touchesEnded : withEvent : 訊息。 每一個這些方法的,是與觸摸階段(例如UITouchPhaseBegan) ,其中任何UItouch物件,你可以找到評估它階段的屬性。 每 一個訊息,就是引用一個事件的處理方法,傳送兩個參數。第一,是一套UItouch物件所代表的新的或經改變觸摸,涉及為特定階段。第二個參數是一個 UIEvent物件代表這個特別的事件。從事件物件,你可以得到所有接觸物件為事件( allTouches )觸摸物件的一個子集,過濾為View或Windows 。一些這些觸摸物件代表觸及這並沒有改變,自上次事件信息的,或已改變,但在不同階段進行。 回 應者物件經常處理的事件,為某一特定階段所獲得一種或一種以上的UItouch物件在傳送一套,然後再評估其屬性或獲得它們的位置。(如果有任何觸摸物件 會做,可採取送NSSet物件的一個anyObject信息)的一個重要方法是locationInView :,其中,如果傳送self的參數, 產生的位置觸摸在回應者物件坐標系統(previousLocationInView:) 。一個平行的方法告訴你,上次的位置觸摸( previousLocationInView: ) 。UITouch的屬性 案例(instance)告訴你有多輕拍(taps)已作出( tapCount ),當觸摸己建立或最後己變化(timestamp)且是否觸摸被強拍(swipe),如果這樣,在哪個方向(info)。 處理輕拍的手勢 一 件很平常的手勢,在iPhone的應用程式中是輕拍(tap):使用者他或她的手指輕拍(taps)一個物件。回應者物件能回應一個單一的輕拍中的一個方 法,一個雙輕拍(double-tap)在另一個,並可能分三輕拍(tripe-tap),但另一種方式表達。以確定有多少次使用者輕拍回應者物件,你會 得到tapCount屬性值的一個UITouch物件。 最好的地方找到這個值(Value)所採用的方法touchesBegan : withEvent :和touchesEnded : withEvent :。在很多情況下,後者的方法是首選,因為它對的回應至於觸摸階段在使用者輕離開拍。由尋找該輕拍的計算(count)輕拍後往上(touch-up)階 段( UITouchPhaseEnded ) ,你確保手指,實在是打字中,比如,在案例,輕拍下(touching down)然後拖曳(dragging)。 - (void) touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { UIScrollView *scrollView = (UIScrollView*)[self superview]; UITouch *touch = [touches anyObject]; CGSize size; CGPoint point; if([touch tapCount] == 2) { if(![_viewController _isZoomed]) { point = [touch locationInView:self]; size = [self bounds].size; point.x /= size.width; point.y /= size.height; [_viewController _setZoomed:YES]; size = [scrollView contentSize]; point.x *= size.width; point.y *= size.height; size = [scrollView bounds].size; point.x -= size.width / 2; point.y -= size.height / 2; [scrollView setContentOffset:point animated:NO]; } else [_viewController _setZoomed:NO]; } } 一 個複雜出現時,回應者物件要處理的單輕拍和雙輕手勢有不同的方式。舉例來說,一個單輕拍可能會選擇物件和一個雙輕拍可能會顯示View為編輯該項目這就是 雙輕拍。又是怎樣回應者物件知道,就是單輕拍,是不是第一次的一個組成部分雙輕裡是如何回應者物件可以處理這種情況使用事件處理方法只是描述: 1. 2. 3. 處理重拍的手勢 當 使用者非常迅速的一個或一個以上的手指穿過螢幕時,系統認為這是重拍的手勢。重拍手勢常常被用來滾動的內容或去向前和向後的一個相連的一系列項目。該系統 監視涉及在屏幕上動作,如果用手指的方向和速度都是一致可能是重拍手勢,它合集info屬性的相關UITouch物件; 屬性表明不僅發生一重拍,但其方向。由於這種檢測需要跟踪的觸摸隨著時間的推移,行蹤,可在一個UITouchPhaseMoved階段,並沒有被標示為 一重拍,即使後來變成了有標記的,因為重拍。 注:一重拍手勢,是編程等於一快速輕拍手勢,雖然他們有不同的人機界面的內涵。還能用,快速輕拍是使用轉移頁面之間的內容,如照片。一重拍,是用來改變一些元素;舉例說,重拍在一封電子郵件上,造成刪除按鈕出現。 但 是,該系統可能稍後不標註觸摸作為重拍,如果隨後跟踪的手指顯示了運動不一致一重拍的手勢。如果一個回應者物件在尋求一個重拍在一個特定方向,並沒有理 會,最後重拍觸摸的狀態,它可以測試info屬性的任何一項UIResponder事件的處理方法。一般來說,你最有興趣,是否觸動作是一重拍,在觸摸物 件的UITouchPhaseEnded階段。你會因此尋找和評估,重拍常數在touchesEnded : withEvent :方法。 列表 8-2處理重拍的手勢 -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { UITouch *touch = [touches anyObject]; if (touch.info & UITouchInfoSwipedRight) { [self showPreviousImage]; } else if (touch.info & UITouchInfoSwipedLeft) { [self showNextImage]; } return; } 該 方法取得了一個觸摸物件從通過在設置評估bit mark 物件的info屬性,以確定是否UITouchInfoSwipedRight和 UITouchInfoSwipedLeft是常量。注意,當該系統探測到重拍的手勢,在一個方向,如"向右上角的螢幕上" ,它規定了兩個常數bit mark比,以顯示這個方向(例如, UITouchInfoSwipedRight和UITouchInfoSwipedUp ) 。 在處理複雜的多點觸控順序 輕拍(taps)及重拍(swipes)是簡單的手勢。處理多點觸控摸順序是更為複雜-效果上,詮釋一個特定應用手勢取決於哪些應用程式正試圖完成的任務。你可能要跟踪所有涉觸模的階段,錄製觸摸屬性已改變,變更內部狀態的。 最好的方法傳達你如何可能處理複雜的多點觸控摸順序,用一個例子。 列表8-3日,顯示出如何定制UIView物件回應觸及動態的"歡迎"的標語牌圍繞在螢幕上當用手指的動作,當用戶重拍的手勢,並改變歡迎的語言的 。 (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [[event allTouches] anyObject]; // Only move the placard view if the touch was in the placard view if ([touch view] != placardView) { // On double tap outside placard view, update placard's display string if ([touch tapCount] == 2) { [placardView setupNextDisplayString]; } return; } // "Pulse" the placard view by scaling up then down // Use UIView's built-in animation [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.5]; CGAffineTransform transform = CGAffineTransformMakeScale(1.2, 1.2); placardView.transform = transform; [UIView commitAnimations]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.5]; transform = CGAffineTransformMakeScale(1.1, 1.1); placardView.transform = transform; [UIView commitAnimations]; // Move the placardView to under the touch [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.25]; placardView.center = [self convertPoint:[touch locationInView:self] fromView:placardView]; [UIView commitAnimations]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [[event allTouches] anyObject]; // If the touch was in the placardView, move the placardView to its location if ([touch view] == placardView) { CGPoint location = [touch locationInView:self]; location = [self convertPoint:location fromView:placardView]; placardView.center = location; return; } } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [[event allTouches] anyObject]; // If the touch was in the placardView, bounce it back to the center if ([touch view] == placardView) { // Disable user interaction so subsequent touches don't interfere with animation self.userInteractionEnabled = NO; [self animatePlacardViewToCenter]; return; } } 注:定制客戶化View,重新劃定自己在回響應事件的處理,一般只應制定一套狀態在事件的處理方法,並履行所有的繪畫在drawrect :方法。要了解更多關於提請查看內容,參見" Graphics and Drawing. " 。 程式碼則表8-4說明了兩種不同的技術。首先,它處理所有接觸階段,在touchesChangedWithEvent :方法,取代由個人接觸階段方法。其次,它處理多點接觸,利用每一個接觸去演奏"prong" ,在模擬樂器( a kalimba ) 列表8-4處理多點觸控在 touchesChangedWithEvent: - (void)touchesChangedWithEvent:(UIEvent *)event { // if the instrument body is showing... if (self.frontShowing) { //iterate through all the touches currently active NSSet *touches = [event allTouches]; for (UITouch *myTouch in touches) { CGPoint location = [myTouch locationInView:self]; //if it's a new touch if (myTouch.phase == UITouchPhaseBegan) { //test for all kalimba prongs NSInteger i; for (i = 0; i < NUMBER_OF_PRONGS ; i++) { if (CGRectContainsPoint(hitRectPlays, location)) { //got one... play the note and make it glow [[InstrumentModel sharedInstrumentModel] playNoteForRegion:(i)]; [[self.glowLayers objectAtIndex:i] setHidden: NO]; } } } else if (myTouch.phase == UITouchPhaseMoved) { CGPoint oldLocation = [myTouch previousLocationInView:self]; // if touch moved off a prong, stop glowing NSInteger i; for (i = 0; i < NUMBER_OF_PRONGS ; i++) { if (CGRectContainsPoint(hitRectPlays, oldLocation) && !CGRectContainsPoint(hitRectPlays, location)) { [[self.glowLayers objectAtIndex:i] setHidden: YES]; } } } else if (myTouch.phase == UITouchPhaseEnded) { // if touch ends on a prong, stop glowing NSInteger i; for (i = 0; i < NUMBER_OF_PRONGS ; i++) { if (CGRectContainsPoint(hitRectPlays, location)) { [[self.glowLayers objectAtIndex:i] setHidden: YES]; } } } } } } 注:程式碼用在列表8-3,來自moveme 的sample,你可以深入研究,以獲得更深入的了解事件的處理情況。 事件處理技術 在 你的事件處理程式碼,你可以儲存有關接觸位元(bit)狀態,為後來的比較與變化UITouch案例(instance)。作為一個例子,說你要比較的最 後位置,每一個接觸到它原來的位置。在touchesBegan : withEvent :方法,你還可以得到原來的位置,每一個觸摸,從locationInView屬性和儲存那些在CFDictionary物件使用地址的uitouch物 件作為key。其後,在touchesended : withevent使用方法:你可以使用的地址,每個通過在uitouch物件,以獲取該物件的原始位置,並比較其目前的位置。(你應該使用 cfdictionary物件,而不是一個nsdictionary物件;後者的複製品,其key,但uitouch纇別並不採用 nscopying議定書,其中規定,為物件拷貝) 。 .Hit-testing就觸摸在subview或Layer - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { CGPoint location = [[touches anyObject] locationInView:self]; CALayer *hitLayer = [[self layer] hitTest:[self convertPoint:location fromView:nil]]; if (hitLayer == infoImage) { [self displayInfo]; } } . 如果你有一個客制的View與subviews ,你需要來決定是否需要處理涉及在subview等級或superview等級。如果subviews不辦處理實作touchesBegan : withEvent : , touchesEnded : withEvent : , touchesMoved : withEvent :,或touchesChangedWithEvent : ,然後這些訊息傳達了回應者鏈向superview 。但是,由於多種輕拍及多重觸及正相關,與subviews那裡,他們第一次發生, superview將不會接受這些觸及。為確保接待,對各種觸及, superview應凌駕hitTest : withEvent :要恢復本身,而不是它的任何subviews 。 .決定當最後手指在一個多點摸序列已解除 - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { if ([touches count] == [[event touchesForView:self] count]) { // last finger has lifted.... } } |
最新回應
1 日 11 小時 前
6 日 31 分鐘 前
1 週 5 小時 前
1 週 3 日 前
1 週 5 日 前
1 週 6 日 前
2 週 19 分鐘 前
2 週 1 日 前
2 週 2 日 前
3 週 2 日 前