手勢識別器html
Gesture recognizers convert low-level event handling code into higher-level actions. They are objects that you attach to a view, which allows the view to respond to actions the way a control does. Gesture recognizers interpret touches to determine whether they correspond to a specific gesture, such as a swipe, pinch, or rotation. If they recognize their assigned gesture, they send an action message to a target object. The target object is typically the view’s view controller, which responds to the gesture as shown in Figure 1-1. This design pattern is both powerful and simple; you can dynamically determine what actions a view responds to, and you can add gesture recognizers to a view without having to subclass the view.ios
手勢識別把低層次的事件處理代碼轉換爲高層次的操做。 它們是你鏈接到視圖的各類對象,它們容許視圖像控件同樣響應各類操做。 手勢識別翻譯(interpret)各類觸摸來決定它們是否響應一個特定的手勢,好比一個點擊(swipe), 捏合(pinch),或旋轉。 若是它們識別到它們指定的手勢,就給目標對象發送一個操做信息。 目標對象一般是視圖的視圖控制器,它響應如圖1-1中所示的手勢。該設計模式既有力又簡單;你能夠動態地決定視圖響應什麼操做,你也能夠給視圖添加各類手勢識別而沒必要要子類化該視圖。設計模式
A gesture recognizer attached to a viewxcode
圖1-1 一個鏈接到視圖的手勢識別app
1、使用手勢識別器來簡化事件處理框架
The UIKit framework provides predefined gesture recognizers that detect common gestures. It’s best to use a predefined gesture recognizer when possible because their simplicity reduces the amount of code you have to write. In addition, using a standard gesture recognizer instead of writing your own ensures that your app behaves the way users expect.less
UIKit 框架提供了一些已經預先定義好的手勢識別來偵測各類經常使用手勢。若是可能的話,最好是使用預約義的手勢識別,由於它們縮減了你必須寫的代碼總量。另外,使用一個標準的手勢識別而不是本身編寫以確保你的應用程序如用戶指望的那樣工做。ide
If you want your app to recognize a unique gesture, such as a checkmark or a swirly motion, you can create your own custom gesture recognizer. To learn how to design and implement your own gesture recognizer, see 「Creating a Custom Gesture Recognizer.」學習
若是你想你的應用程序識別一個獨特的手勢,好比一個勾或一個漩渦狀運動,你能夠建立你本身的自定義手勢識別。 學習如何設計和實現你本身的手勢識別,請看 「Creating a Custom Gesture Recognizer.」動畫
一、內建手勢識別來識別各類經常使用手勢
When designing your app, consider what gestures you want to enable. Then, for each gesture, determine whether one of the predefined gesture recognizers listed in Table 1-1 is sufficient.
當你設計應用程序時,考慮你想要開啓什麼手勢。 而後,對於每一個手勢,決定列表1-1中的預約義手勢識別是否就足夠。
Gesture |
UIKit class |
---|---|
Tapping (any number of taps) |
|
Pinching in and out (for zooming a view) |
|
Panning or dragging |
|
Swiping (in any direction) |
|
Rotating (fingers moving in opposite directions) |
|
Long press (also known as 「touch and hold」) |
Your app should respond to gestures only in ways that users expect. For example, a pinch should zoom in and out whereas a tap should select something. For guidelines about how to properly use gestures, see 「Apps Respond to Gestures, Not Clicks」 in iOS Human Interface Guidelines.
你的應用程序應該只以用戶期待的方式來響應。 好比,一個捏合動做應該縮放,而輕擊動做應該選擇一些東西。 關於正確使用手勢的指南,請看iOS Human Interface Guidelines 中的 「Apps Respond to Gestures, Not Clicks」。
二、手勢識別鏈接到視圖
Every gesture recognizer is associated with one view. By contrast, a view can have multiple gesture recognizers, because a single view might respond to many different gestures. For a gesture recognizer to recognize touches that occur in a particular view, you must attach the gesture recognizer to that view. When a user touches that view, the gesture recognizer receives a message that a touch occurred before the view object does. As a result, the gesture recognizer can respond to touches on behalf of the view.
每一個手勢識別都跟一個視圖相關聯。 一般,視圖能夠有多個手勢識別,由於單個視圖可能響應多個不一樣的手勢。 要想一個手勢識別可以識別在一個特殊視圖上發生的各類觸摸,你必須把手勢識別鏈接到那個視圖。 當用戶觸摸了那個視圖,手勢識別先於視圖對象接收一個觸摸發生的信息。 結果,手勢識別能夠經過視圖的行爲來響應各類觸摸。
三、 手勢觸發操做消息
When a gesture recognizer recognizes its specified gesture, it sends an action message to its target. To create a gesture recognizer, you initialize it with a target and an action.
當一個手勢識別識別出其制定的手勢後,它給它的目標發送一個操做消息。 要想建立一個手勢識別,你須要初始化一個目標和一個操做。
1). 離散和連續的手勢
Gestures are either discrete or continuous. A discrete gesture, such as a tap, occurs once. A continuous gesture, such as pinching, takes place over a period of time. For discrete gestures, a gesture recognizer sends its target a single action message. A gesture recognizer for continuous gestures keeps sending action messages to its target until the multitouch sequence ends, as shown in Figure 1-2.
手勢有離散手勢,也有連續手勢。 一個離散手勢,好比一個輕擊(tap),只發生一次。一個連續手勢,好比捏合(pinching),發生時持續一段時間。 對於離散手勢,手勢識別就給目標發送一個操做消息。對於連續手勢,手勢識別則保持發送操做消息給目標直到多點觸摸序列結束,如圖1-2.
Discrete and continuous gestures
圖1-2 離散和連續手勢
2、用手勢識別響應事件
There are three things you do to add a built-in gesture recognizer to your app:
你須要作三件事來添加一個內建手勢識別到應用程序:
Create and configure a gesture recognizer instance.
建立並配置一個手勢識別實例。
This step includes assigning a target, action, and sometimes assigning gesture-specific attributes (such as the numberOfTapsRequired
).
該步驟包括分配一個目標,操做,以及有時候分配手勢指定的各類屬性(好比 numberOfTapsRequired).
Attach the gesture recognizer to a view.
把手勢識別鏈接到一個視圖。
Implement the action method that handles the gesture.
實現處理手勢的操做方法。
一、使用界面生成器(Interface Builder)來添加一個手勢識別到應用程序
Within Interface Builder in Xcode, add a gesture recognizer to your app the same way you add any object to your user interface—drag the gesture recognizer from the object library to a view. When you do this, the gesture recognizer automatically becomes attached to that view. You can check which view your gesture recognizer is attached to, and if necessary, change the connection in the nib file.
在Xcode裏的界面生成器中,添加手勢識別到應用程序跟你添加任何對象到用戶界面(add any object to your user interface)是同樣的---就是從對象庫中拖拉手勢識別到一個視圖。完成該操做後,手勢識別自動鏈接到那個視圖。 你能夠檢查手勢識別被鏈接到了哪一個視圖,而且若是必要,在nib 文件中更改該鏈接(change the connection in the nib file)。
After you create the gesture recognizer object, you need to create and connect an action method. This method is called whenever the connected gesture recognizer recognizes its gesture. If you need to reference the gesture recognizer outside of this action method, you should also create and connect an outlet for the gesture recognizer. Your code should look similar to Listing 1-1.
當你建立完手勢識別對象後,你須要建立並鏈接一個操做方法(create and connect an action)。任什麼時候候手勢識別(gesture recognizer)識別手勢時都將調用該方法。若是你須要在該操做方法外面引用手勢識別,你還應該爲手勢識別建立並鏈接一個輸出口 (create and connect an outlet)。你的代碼應該跟列表1-1類似.
Adding a gesture recognizer to your app with Interface Builder
列表1-1 用界面生成器添加一個手勢識別到應用程序
@interface APLGestureRecognizerViewController () |
@property (nonatomic, strong) IBOutlet UITapGestureRecognizer *tapRecognizer; |
@end |
@implementation |
- (IBAction)displayGestureForTapRecognizer:(UITapGestureRecognizer *)recognizer |
// Will implement method later... |
} |
@end |
二、經過程序添加一個手勢識別
You can create a gesture recognizer programmatically by allocating and initializing an instance of a concrete UIGestureRecognizer
subclass, such asUIPinchGestureRecognizer
. When you initialize the gesture recognizer, specify a target object and an action selector, as in Listing 1-2. Often, the target object is the view’s view controller.
你能夠經過分配和初始化一個具體(concrete)的UIGestureRecognizer 子類的實例來建立一個手勢識別,好比 UIPinchGestureRecognizer 。 當你初始化手勢識別時,指定一個目標對象和一個操做選擇器(selector),正如列表1-2中所示,一般目標對象就是視圖的視圖控制器。
If you create a gesture recognizer programmatically, you need to attach it to a view using the addGestureRecognizer:
method. Listing 1-2 creates a single tap gesture recognizer, specifies that one tap is required for the gesture to be recognized, and then attaches the gesture recognizer object to a view. Typically, you create a gesture recognizer in your view controller’s viewDidLoad
method, as shown in Listing 1-2.
若是你經過程序建立一個手勢識別,你須要調用addGestureRecognizer: 方法來把它鏈接到視圖。 列表1-2 建立了一個單個輕擊手勢識別,指定識別該手勢須要一次輕擊(tap),而後把手勢識別對象鏈接到一個視圖。 一般,你在視圖控制器的
viewDidLoad 方法裏建立手勢識別,如列表1-2所示。
Creating a single tap gesture recognizer programmatically
列表1-2 經過程序建立一個單一的輕擊手勢識別
- (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]; |
// Do any additional setup after loading the view, typically from a nib |
} |
三、響應離散手勢
When you create a gesture recognizer, you connect the recognizer to an action method. Use this action method to respond to your gesture recognizer’s gesture.Listing 1-3 provides an example of responding to a discrete gesture. When the user taps the view that the gesture recognizer is attached to, the view controller displays an image view that says 「Tap.」 The showGestureForTapRecognizer:
method determines the location of the gesture in the view from the recognizer’slocationInView:
property and then displays the image at that location.
當你建立一個手勢識別時,你把該識別(recognizer)鏈接到一個操做方法。使用該操做方法來響應手勢識別的手勢。列表1-3 提供了一個響應一個離散手勢的例子。 當用戶輕擊了帶有手勢識別的視圖時,視圖控制器顯示一個圖像視圖(image view)表示"Tap"發生。 showGestureForTapRecognizer: 方法決定了視圖中手勢的位置,該位置從recognizer的locationInView: 特性中獲取,而後把圖片顯示到該位置。
Note: The next three code examples are from the Simple Gesture Recognizers sample code project, which you can examine for more context.
注意: 接下來的3段代碼例子取自 Simple Gesture Recognizers 樣本代碼工程,你能夠查看它以得到更多上下文。
Handling a double tap gesture
列表1-3 處理一個雙擊手勢
- (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; |
}]; |
} |
Each gesture recognizer has its own set of properties. For example, in Listing 1-4, the showGestureForSwipeRecognizer:
method uses the swipe gesture recognizer’s direction
property to determine if the user swiped to the left or to the right. Then, it uses that value to make an image fade out in the same direction as the swipe.
每一個手勢識別都有它本身的特性集。好比,如列表1-4中,showGestureForSwipeRecognizer: 方法使用點擊(swipe)手勢識別的direction 特性來決定用戶是向左清掃仍是向右。 而後,它使用該值來讓圖像從點擊方向消失。
Responding to a left or right swipe gesture
列表1-4 響應一個向左或向右清掃手勢
// Respond to a swipe gesture |
- (IBAction)showGestureForSwipeRecognizer:(UISwipeGestureRecognizer *)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]; |
// If gesture is a left swipe, specify an end location |
// to the left of the current location |
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft) { |
location.x -= 220.0; |
} else { |
location.x += 220.0; |
} |
// Animate the image view in the direction of the swipe as it fades out |
[UIView animateWithDuration:0.5 animations:^{ |
self.imageView.alpha = 0.0; |
self.imageView.center = location; |
}]; |
} |
四、響應連續手勢
Continuous gestures allow your app to respond to a gesture as it is happening. For example, your app can zoom while a user is pinching or allow a user to drag an object around the screen.
連續手勢容許應用程序在手勢發生時響應。 好比,用戶能夠一邊作捏合手勢,一邊就能夠看見縮放,或者容許用戶沿着屏幕拖拉一個對象。
Listing 1-5 displays a 「Rotate」 image at the same rotation angle as the gesture, and when the user stops rotating, animates the image so it fades out in place while rotating back to horizontal. As the user rotates his fingers, the showGestureForRotationRecognizer:
method is called continually until both fingers are lifted.
列表1-5 以跟手勢一樣的角度顯示一個"Rotate"圖片,而且當用戶中止旋轉時,動畫該圖片讓其在旋轉回水平方向的同時消失。 當用戶旋轉他的手指時,持續調用 showGestureForRotationRecognizer: 方法直到兩個手指都拿起。
Responding to a rotation gesture
列表1-5 響應一個旋轉手勢
// Respond to a rotation gesture |
- (IBAction)showGestureForRotationRecognizer:(UIRotationGestureRecognizer *)recognizer { |
// Get the location of the gesture |
CGPoint location = [recognizer locationInView:self.view]; |
// Set the rotation angle of the image view to |
// match the rotation of the gesture |
CGAffineTransform transform = CGAffineTransformMakeRotation([recognizer rotation]); |
self.imageView.transform = transform; |
// Display an image view at that location |
[self drawImageForGestureRecognizer:recognizer atPoint:location]; |
// If the gesture has ended or is canceled, begin the animation |
// back to horizontal and fade out |
if (([recognizer state] == UIGestureRecognizerStateEnded) || ([recognizer state] == UIGestureRecognizerStateCancelled)) { |
[UIView animateWithDuration:0.5 animations:^{ |
self.imageView.alpha = 0.0; |
self.imageView.transform = CGAffineTransformIdentity; |
}]; |
} |
} |
Each time the method is called, the image is set to be opaque in the drawImageForGestureRecognizer:
method. When the gesture is complete, the image is set to be transparent in the animateWithDuration:
method. The showGestureForRotationRecognizer:
method determines whether a gesture is complete by checking the gesture recognizer’s state. These states are explained in more detail in 「Gesture Recognizers Operate in a Finite State Machine.」
每次調用該方法時,圖片都在drawImageForGestureRecognizer: 方法中被設置爲不透明。 當手勢完成時,圖片在animateWithDuration: 方法中被設置爲透明。showGestureForRotationRecognizer:方法經過檢查手勢識別的狀態來判斷手勢是否完成。 關於這些狀態的更多信息,請看「Gesture Recognizers Operate in a Finite State Machine.」
3、定義手勢識別如何交互
Oftentimes, as you add gesture recognizers to your app, you need to be specific about how you want the recognizers to interact with each other or any other touch-event handling code in your app. To do this, you first need to understand a little more about how gesture recognizers work.
一般狀況下,當你把手勢識別添加到應用程序時,你須要瞭解你想要你的識別(recognizers)之間如何發生交互,或者跟應用程序的其它任何觸摸事件處理代碼如何發生交互。要想完成這些,你首先須要理解更多點關於手勢識別如何工做的知識。
一、手勢識別在一個有限狀態機裏操做
Gesture recognizers transition from one state to another in a predefined way. From each state, they can move to one of several possible next states based on whether they meet certain conditions. The exact state machine varies depending on whether the gesture recognizer is discrete or continuous, as illustrated inFigure 1-3. All gesture recognizers start in the Possible state (UIGestureRecognizerStatePossible
). They analyze any multitouch sequences that they receive, and during analysis they either recognize or fail to recognize a gesture. Failing to recognize a gesture means the gesture recognizer transitions to the Failed state (UIGestureRecognizerStateFailed
).
手勢識別以一種預約義的方式從一個狀態過渡到另外一個狀態。 每種狀態均可以根據它們遇到的特定條件(conditions)過渡到幾種可能的將來狀態中的一種。確切的狀態機根據手勢識別是離散或是連續會發生變化,正如圖1-3中所示。全部的手勢識別在一個可能的狀態(UIGestureRecognizerStatePossible
)中開始, 它們分析接收到的任何多點觸摸序列,而且在分析過程當中成功識別手勢或者識別識別一個手勢。失敗識別手勢意味着手勢識別過渡到失敗狀態 (UIGestureRecognizerStateFailed
).
State machines for gesture recognizers
圖1-3 手勢識別的狀態機
When a discrete gesture recognizer recognizes its gesture, the gesture recognizer transitions from Possible to Recognized (UIGestureRecognizerStateRecognized
) and the recognition is complete.
當一個離散手勢識別識別出它的手勢,手勢識別從可能狀態過渡到被識別狀態 (UIGestureRecognizerStateRecognized
) , 而後識別完成。
For continuous gestures, the gesture recognizer transitions from Possible to Began (UIGestureRecognizerStateBegan
) when the gesture is first recognized. Then, it transitions from Began to Changed (UIGestureRecognizerStateChanged
), and continues to move from Changed to Changed as the gesture occurs. When the user’s last finger is lifted from the view, the gesture recognizer transitions to the Ended state (UIGestureRecognizerStateEnded
) and the recognition is complete. Note that the Ended state is an alias for the Recognized state.
對於連續手勢,當手勢第一次被識別時,手勢識別從可能狀態開始(UIGestureRecognizerStateBegan
)。而後,它從開始狀態過渡到改變狀態(UIGestureRecognizerStateChanged
), 而後當手勢發生時又從改變狀態變爲改變狀態。 當用戶的最後一個手指從視圖上舉起時,手勢識別過渡到結束狀態(UIGestureRecognizerStateEnded
), 識別完成。 注意結束狀態是識別完成狀態的一個別名。
A recognizer for a continuous gesture can also transition from Changed to Canceled (UIGestureRecognizerStateCancelled
) if it decides that the gesture no longer fits the expected pattern.
若是一個連續手勢再也不適應預期的模式時,它的識別還能夠從改變狀態過渡到取消狀態(UIGestureRecognizerStateCancelled
)。
Every time a gesture recognizer changes state, the gesture recognizer sends an action message to its target, unless it transitions to Failed or Canceled. Thus, a discrete gesture recognizer sends only a single action message when it transitions from Possible to Recognized. A continuous gesture recognizer sends many action messages as it changes states.
每次手勢識別改變狀態時,手勢識別都給它的目標發送一個操做消息,除非它過渡到失敗或取消狀態。 然而,一個離散手勢識別只在它從可能狀態過渡到被識別狀態時才發送一個單個操做消息。一個連續手勢識別在狀態發生改變時發送多個操做消息。
When a gesture recognizer reaches the Recognized (or Ended) state, it resets its state back to Possible. The transition back to Possible does not trigger an action message.
當一個手勢識別到達被識別(或結束)狀態時,它把狀態重置爲可能(Possible)狀態。把狀態重置爲可能狀態不會觸發一個操做消息。
二、跟其它手勢識別發生交互
A view can have more than one gesture recognizer attached to it. Use the view’s gestureRecognizers
property to determine what gesture recognizers are attached to a view. You can also dynamically change how a view handles gestures by adding or removing a gesture recognizer from a view with theaddGestureRecognizer:
and removeGestureRecognizer:
methods, respectively.
一個視圖能夠帶有多個手勢。使用視圖的gestureRecognizers 特性來肯定視圖都帶有哪些手勢識別。 你還能夠分別(respectively)使用addGestureRecognizer: 方法和removeGestureRecognizer: 方法添加和刪除視圖上的手勢識別來動態改變視圖如何處理手勢。
When a view has multiple gesture recognizers attached to it, you may want to alter how the competing gesture recognizers receive and analyze touch events. By default, there is no set order for which gesture recognizers receive a touch first, and for this reason touches can be passed to gesture recognizers in a different order each time. You can override this default behavior to:
當視圖帶有多個手勢識別時,你可能想要改變競爭(competing)手勢識別是如何接收和分析觸摸事件。默認狀況下,沒有設置哪一個手勢識別首先接收到第一個觸摸,所以每次觸摸均可以以不一樣的順序傳送給手勢識別。 你能夠重寫該默認行爲來:
Specify that one gesture recognizer should analyze a touch before another gesture recognizer.
Allow two gesture recognizers to operate simultaneously.
Prevent a gesture recognizer from analyzing a touch.
Use the UIGestureRecognizer
class methods, delegate methods, and methods overridden by subclasses to effect these behaviors.
使用UIGestureRecognizer 類方法,委託方法,以及其子類重寫的方法來影響這些行爲。
1)爲兩個手勢識別聲明一個特定順序
Imagine that you want to recognize a swipe and a pan gesture, and you want these two gestures to trigger distinct actions. By default, when the user attempts to swipe, the gesture is interpreted as a pan. This is because a swiping gesture meets the necessary conditions to be interpreted as a pan (a continuous gesture) before it meets the necessary conditions to be interpreted as a swipe (a discrete gesture).
想象一下,你想要識別一個快速滑動(swipe)和一個慢速拖動(pan)手勢,你想要用這兩個手勢觸發不一樣的操做。默認狀況下,當用戶嘗試swipe時,該手勢會被理解爲一個pan。 這是由於swipe(一個離散手勢)手勢在知足各類必要條件被理解爲一個swipe手勢以前,首先知足pan(一個連續手勢)手勢的各類必要條件。
For your view to recognize both swipes and pans, you want the swipe gesture recognizer to analyze the touch event before the pan gesture recognizer does. If the swipe gesture recognizer determines that a touch is a swipe, the pan gesture recognizer never needs to analyze the touch. If the swipe gesture recognizer determines that the touch is not a swipe, it moves to the Failed state and the pan gesture recognizer should begin analyzing the touch event.
要想視圖同時識別swipes 和pans,你想要swipe手勢識別在pan手勢以前來分析觸摸事件。 若是swipe手勢識別已經肯定某個觸摸是一個swipe, 那麼pan手勢識別就絕沒有必要再去分析該觸摸。 若是swipe手勢識別肯定該觸摸不是一個swipe, 它過渡到Failed狀態,而後pan手勢識別應該開始分析該觸摸事件。
You indicate this type of relationship between two gesture recognizers by calling the requireGestureRecognizerToFail:
method on the gesture recognizer that you want to delay, as in Listing 1-6. In this listing, both gesture recognizers are attached to the same view.
你能夠經過調用手勢識別的requireGestureRecognizerToFail: 方法來講明兩個手勢識別之間這種類型的關係,如列表1-6所示。在該列表中,兩個手勢識別都被鏈接到了相同的視圖上。
Pan gesture recognizer requires a swipe gesture recognizer to fail
列表1-6 pan手勢識別要求swipe手勢識別到達fail狀態
- (void)viewDidLoad { |
[super viewDidLoad]; |
// Do any additional setup after loading the view, typically from a nib |
[self.panRecognizer requireGestureRecognizerToFail:self.swipeRecognizer]; |
} |
The requireGestureRecognizerToFail:
method sends a message to the receiver and specifies some otherGestureRecognizer that must fail before the receiving recognizer can begin. While it’s waiting for the other gesture recognizer to transition to the Failed state, the receiving recognizer stays in the Possible state. If the other gesture recognizer fails, the receiving recognizer analyzes the touch event and moves to its next state. On the other hand, if the other gesture recognizer transitions to Recognized or Began, the receiving recognizer moves to the Failed state. For information about state transitions, see 「Gesture Recognizers Operate in a Finite State Machine.」
requireGestureRecognizerToFail: 方法給接收者發送了一個消息,而且指定了一些otherGestureRecognizer,它們必須在接收識別(receiving recognizer)開始工做以前進入Failed 狀態。當它等待其它手勢識別過渡到Failed狀態期間,接收識別(receiving recognizer)始終處於Possible狀態。若是其它手勢識別失敗了,receiving recognizer 開始分析觸摸事件並移動到下個狀態。換句話說,若是其它手勢識別過渡到Recognized 或者 Began狀態,receiving recognizer就移動到Failed 狀態。 關於狀態過渡的更多信息, 請看「Gesture Recognizers Operate in a Finite State Machine.」
Note: If your app recognizes both single and double taps and your single tap gesture recognizer does not require the double tap recognizer to fail, then you should expect to receive single tap actions before double tap actions, even when the user double taps. This behavior is intentional because the best user experience generally enables multiple types of actions.
注意:若是你的應用程序識別同時支持單擊和雙擊,而且你的單擊手勢識別不要求雙擊識別失敗,那麼即便是用戶雙擊時,你也應該期待在雙擊操做以前接收單擊操做。該行爲是有意設計的,由於最好的用戶體驗一般都開啓多種類型的操做。
If you want these two actions to be mutually exclusive, your single tap recognizer must require the double tap recognizer to fail. However, your single tap actions will lag a little behind the user’s input because the single tap recognizer is delayed until the double tap recognizer fails.
若是你想要這兩種操做不兼容,你的單擊識別必需要求雙擊識別進入失敗狀態。 然而,這樣你的單擊操做會滯後用戶的輸入,由於單擊識別會等到雙擊識別失敗後纔開始識別。
2)阻止手勢識別分析觸摸
You can alter the behavior of a gesture recognizer by adding a delegate object to your gesture recognizer. The UIGestureRecognizerDelegate
protocol provides a couple of ways that you can prevent a gesture recognizer from analyzing touches. You use either the gestureRecognizer:shouldReceiveTouch:
method or the gestureRecognizerShouldBegin:
method—both are optional methods of the UIGestureRecognizerDelegate
protocol.
你能夠經過添加一個委託對象到手勢識別來改變手勢識別的行爲。UIGestureRecognizerDelegate 協議提供了一組方法來阻止手勢識別分析觸摸。 你能夠選擇使用協議中的
gestureRecognizer:shouldReceiveTouch: 方法 和 gestureRecognizerShouldBegin: 方法中的一個來使用。
When a touch begins, if you can immediately determine whether or not your gesture recognizer should consider that touch, use thegestureRecognizer:shouldReceiveTouch:
method. This method is called every time there is a new touch. Returning NO
prevents the gesture recognizer from being notified that a touch occurred. The default value is YES
. This method does not alter the state of the gesture recognizer.
當一個觸摸開始時,若是你能夠當即肯定手勢識別是否應該考慮該觸摸,使用 gestureRecognizer:shouldReceiveTouch: 方法來實現。每次有新觸摸時都調用該方法。 阻止手勢識別注意到一個觸摸的發生,請返回NO。默認值是YES。該方法不改變手勢識別的狀態。
Listing 1-7 uses the gestureRecognizer:shouldReceiveTouch:
delegate method to prevent a tap gesture recognizer from receiving touches that are within a custom subview. When a touch occurs, the gestureRecognizer:shouldReceiveTouch:
method is called. It determines whether the user touched the custom view, and if so, prevents the tap gesture recognizer from receiving the touch event.
列表1-7 使用 gestureRecognizer:shouldReceiveTouch: 委託方法來阻止手勢識別接收到來自一個自定義子視圖中發生的觸摸。
Preventing a gesture recognizer from receiving a touch
- (void)viewDidLoad { |
[super viewDidLoad]; |
// Add the delegate to the tap gesture recognizer |
self.tapGestureRecognizer.delegate = self; |
} |
// Implement the UIGestureRecognizerDelegate method |
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { |
// Determine if the touch is inside the custom subview |
if ([touch view] == self.customSubview){ |
// If it is, prevent all of the delegate's gesture recognizers |
// from receiving the touch |
return NO; |
} |
return YES; |
} |
If you need to wait as long as possible before deciding whether or not a gesture recognizer should analyze a touch, use the gestureRecognizerShouldBegin:
delegate method. Generally, you use this method if you have a UIView
or UIControl
subclass with custom touch-event handling that competes with a gesture recognizer. Returning NO
causes the gesture recognizer to immediately fail, which allows the other touch handling to proceed. This method is called when a gesture recognizer attempts to transition out of the Possible state, if the gesture recognition would prevent a view or control from receiving a touch.
若是你須要在肯定手勢識別是否應該分析一個觸摸以前一直等待。使用 gestureRecognizerShouldBegin: 委託方法。 一般,若是你有一個UIView 或 UIControl子類並帶有跟手勢識別想衝突的自定義觸摸事件處理,你可使用該方法。返回NO,讓手勢識別當即進入失敗狀態,容許其餘觸摸處理來處理。 當手勢識別想要過渡到Possible狀態之外的狀態時,若是手勢識別將阻止一個視圖或控件接收一個觸摸,該方法被調用。
You can use the gestureRecognizerShouldBegin:
UIView
method if your view or view controller cannot be the gesture recognizer’s delegate. The method signature and implementation is the same.
若是你的視圖或視圖控制器不能成爲手勢識別委託,你可使用UIView的fgestureRecognizerShouldBegin:方法 。該方法的簽名和實現是同樣的。
3)開啓同時手勢識別
By default, two gesture recognizers cannot recognize their respective gestures at the same time. But suppose, for example, that you want the user to be able to pinch and rotate a view at the same time. You need to change the default behavior by implementing thegestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
method, an optional method of the UIGestureRecognizerDelegate
protocol. This method is called when one gesture recognizer’s analysis of a gesture would block another gesture recognizer from recognizing its gesture, or vice versa. This method returns NO
by default. Return YES
when you want two gesture recognizers to analyze their gestures simultaneously.
默認狀況下,兩個手勢識別不能同時識別它們的不一樣手勢。可是,假設你想讓用戶能夠同時捏合並旋轉一個視圖,你須要改變默認行爲,你能夠經過調用gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: 方法來實現。該方法是
UIGestureRecognizerDelegate 協議的一個可選方法。 當一個手勢識別的手勢分析可能阻礙另外一個手勢識別識別它的手勢時能夠調用該方法,反之亦然。 該方法默認範圍NO。當你想讓兩個手勢同時分析它們的手勢時,返回YES。
Note: You need to implement a delegate and return YES
on only one of your gesture recognizers to allow simultaneous recognition. However, that also means that returning NO
doesn’t necessarily prevent simultaneous recognition because the other gesture recognizer's delegate could return YES
.
注意:你只在一個手勢識別須要開啓同時識別功能時才須要實現一個委託並返回YES。 然而,它還意味着返回NO並不必定阻止同時識別功能,由於其餘手勢識別的委託可能返回了YES。
4)給兩個手勢指定一個單向關係
If you want to control how two recognizers interact with each other but you need to specify a one-way relationship, you can override either thecanPreventGestureRecognizer:
or canBePreventedByGestureRecognizer:
subclass methods to return NO
(default is YES
). For example, if you want a rotation gesture to prevent a pinch gesture but you don’t want a pinch gesture to prevent a rotation gesture, you would specify:
若是你想要控制兩個識別(recognizers)是如何交互,可是你須要指定一個單向關係,你能夠重寫canPreventGestureRecognizer: 或 canBePreventedByGestureRecognizer: 子類方法並返回NO(默認爲YES)。 好比,若是你想用一個旋轉手勢阻止一個捏合手勢,可是你又不想捏合手勢阻止一個旋轉手勢,你能夠用以下語句指定。
[rotationGestureRecognizer canPreventGestureRecognizer:pinchGestureRecognizer]; |
and override the rotation gesture recognizer’s subclass method to return NO
. For more information about how to subclass UIGestureRecognizer
, see 「Creating a Custom Gesture Recognizer.」
同時,重寫旋轉手勢識別的子類方法來返回NO. 更多管理如何子類化 UIGestureRecognizer的信息,請看 「Creating a Custom Gesture Recognizer.」
If neither gesture should prevent the other, use the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
method, as described in「Permitting Simultaneous Gesture Recognition.」 By default, a pinch gesture prevents a rotation and vice versa because two gestures cannot be recognized at the same time.
若是沒有手勢須要阻止另外手勢,使用gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer: ,它在「Permitting Simultaneous Gesture Recognition.」 裏描述。 默認狀況下,捏合手勢阻止旋轉手勢,或者旋轉手勢阻止捏合手勢,由於兩個手勢不能同時被識別。
5) 跟別的用戶界面控件交互
In iOS 6.0 and later, default control actions prevent overlapping gesture recognizer behavior. For example, the default action for a button is a single tap. If you have a single tap gesture recognizer attached to a button’s parent view, and the user taps the button, then the button’s action method receives the touch event instead of the gesture recognizer. This applies only to gesture recognition that overlaps the default action for a control, which includes:
在iOS 6.0 或之後版本中,默認控件操做方法防止(prevent)重複手勢識別的行爲。好比,一個按鈕的默認操做是一個單擊。若是你有一個單擊手勢識別綁定到一個按鈕的父視圖上,而後用戶點擊該按鈕,最後按鈕的操做方法接收觸摸事件而不是手勢識別。 它只用於手勢識別跟一個控件的默認操做重複時,包括:
A single finger single tap on a UIButton
, UISwitch
, UIStepper
, UISegmentedControl
, and UIPageControl
.
UIButton
, UISwitch
, UIStepper
, UISegmentedControl
, and UIPageControl 上的
單擊。A single finger swipe on the knob of a UISlider
, in a direction parallel to the slider.
UISlider 上的快速滑動(swipe),輕掃方向跟slider平行
A single finger pan gesture on the knob of a UISwitch
, in a direction parallel to the switch.
If you have a custom subclass of one of these controls and you want to change the default action, attach a gesture recognizer directly to the control instead of to the parent view. Then, the gesture recognizer receives the touch event first. As always, be sure to read the iOS Human Interface Guidelines to ensure that your app offers an intuitive user experience, especially when overriding the default behavior of a standard control.
若是你有一個這些控件的自定義子類,你想要改變其默認操做,把手勢識別直接鏈接到控件而不是鏈接到其父視圖。而後,手勢識別首先接收到觸摸事件。一如往常,請確保你已經閱讀了 iOS Human Interface Guidelines 文檔以確保你的應用程序提供了一個直觀的用戶體驗,特別是當你重寫一個標準控件的默認行爲時。
4、手勢識別解讀(interpret)原始觸摸事件
So far, you’ve learned about gestures and how your app can recognize and respond to them. However, to create a custom gesture recognizer or to control how gesture recognizers interact with a view’s touch-event handling, you need to think more specifically in terms of touches and events.
目前位置,你已經學習了關於手勢以及應用程序如何識別並響應它們。 然而,要想建立一個自定義手勢識別或想控制時手勢別如何跟視圖的觸摸事件處理相交互,你須要更具體地(specifically)思考觸摸和事件的方方面面。
一、一個事件包含了單前多點觸摸序列的全部觸摸
In iOS, a touch is the presence or movement of a finger on the screen. A gesture has one or more touches, which are represented by UITouch
objects. For example, a pinch-close gesture has two touches—two fingers on the screen moving toward each other from opposite directions.
在iOS中,一個觸摸是一個手指在屏幕上的存在或運動。 一個手勢有一個或多個觸摸,它由UITouch 對象表示。好比,一個pinch-close手勢有兩個觸摸--兩個手指在屏幕上從相反方向朝着彼此移動。
An event encompasses all touches that occur during a multitouch sequence. A multitouch sequence begins when a finger touches the screen and ends when the last finger is lifted. As a finger moves, iOS sends touch objects to the event. An multitouch event is represented by a UIEvent
object of typeUIEventTypeTouches
.
一個事件包含(encompasses)一個多點觸摸序列的全部觸摸。 一個多點觸摸序列以一個手指觸摸屏幕開始,以最後一個手指離開屏幕結束。 當一個手指移動時,iOS給事件觸摸對象。一個多點觸摸事件由 UIEventTypeTouches 類型的UIEvent 對象表示。
Each touch object tracks only one finger and lasts only as long as the multitouch sequence. During the sequence, UIKit tracks the finger and updates the attributes of the touch object. These attributes include the phase of the touch, its location in a view, its previous location, and its timestamp.
每一個觸摸對象只跟蹤一個手指,而且只在觸摸序列期間跟蹤。 在序列期間,UIKit跟蹤手指並更新觸摸對象的各類屬性。 這些屬性包括觸摸階段(phase),它在視圖中的位置,它的前一個位置,以及它的時間戳。
The touch phase indicates when a touch begins, whether it is moving or stationary, and when it ends—that is, when the finger is no longer touching the screen. As depicted in Figure 1-4, an app receives event objects during each phase of any touch.
觸摸階段代表一個觸摸什麼時候開始,它是移動的仍是靜止的,以及它什麼時候結束---當手指再也不觸摸屏幕的時間。 正如圖1-4所示,應用程序在任何觸摸的每一個階段之間接收事件對象。
A multitouch sequence and touch phases
圖1-4 一個多點觸摸序列和觸摸階段
Note: A finger is less precise than a mouse pointer. When a user touches the screen, the area of contact is actually elliptical and tends to be slightly lower than the user expects. This 「contact patch」 varies based on the size and orientation of the finger, the amount of pressure, which finger is used, and other factors. The underlying multitouch system analyzes this information for you and computes a single touch point, so you don’t need to write your own code to do this.
注意:手指沒有鼠標點擊精確。 當用戶觸摸屏幕時,接觸的區域其實是橢圓的,而且會比用戶期待的位置稍微篇低。 該接觸面會根據手指的尺寸和方向,手指使用時的壓力,以及其它因素的不一樣而發生改變。底層多點觸摸系統會替你分析該信息並計算一個單擊點,所以你不須要本身寫代碼來實現它。
二、應用程序在觸摸處理方法中接收觸摸
During a multitouch sequence, an app sends these messages when there are new or changed touches for a given touch phase; it calls the
在一個多點觸摸序列期間,應用程序在新觸摸發生或者給出的觸摸階段發生改變時發送這些信息;它調用如下方法:
touchesBegan:withEvent:
method when one or more fingers touch down on the screen.
touchesMoved:withEvent:
method when one or more fingers move.
touchesEnded:withEvent:
method when one or more fingers lift up from the screen.
touchesCancelled:withEvent:
method when the touch sequence is canceled by a system event, such as an incoming phone call.
Each of these methods is associated with a touch phase; for example, the touchesBegan:withEvent:
method is associated with UITouchPhaseBegan
. The phase of a touch object is stored in its phase
property.
每一個方法都跟一個觸摸階段相關聯;好比,touchesBegan: 方法跟 UITouchPhaseBegan 方法相關聯。 觸摸對象的階段(phase)被存儲在其
phase 特性裏。
Note: These methods are not associated with gesture recognizer states, such as UIGestureRecognizerStateBegan
and UIGestureRecognizerStateEnded
. Gesture recognizer states strictly denote the phase of the gesture recognizer itself, not the phase of the touch objects that are being recognized.
注意: 這些方法跟手勢識別狀態沒有關聯,好比UIGestureRecognizerStateBegan
和 UIGestureRecognizerStateEnded等。 手勢識別器狀態嚴格表示手勢識別器自身的階段,不表示正在被識別的觸摸對象階段。
5、調節觸摸到視圖的傳遞
There may be times when you want a view to receive a touch before a gesture recognizer. But, before you can alter the delivery path of touches to views, you need to understand the default behavior. In the simple case, when a touch occurs, the touch object is passed from the UIApplication
object to the UIWindow
object. Then, the window first sends touches to any gesture recognizers attached the view where the touches occurred (or to that view’s superviews), before it passes the touch to the view object itself.
可能有時候你想要在手勢識別器以前接收到一個觸摸。可是在你能夠改變觸摸到視圖的傳遞路徑以前,你須要理解其默認行爲。在簡單狀況下,當一個觸摸發生時,觸摸對象從UIApplication對象傳遞到UIWindow對象。 而後,窗口首先把觸摸發送給觸摸發生的視圖上關聯的任何手勢識別器,而不是先發送給視圖對象自身。
Default delivery path for touch events
圖1-5 觸摸事件的默認傳遞路徑
一、手勢識別器首先識別一個觸摸
A window delays the delivery of touch objects to the view so that the gesture recognizer can analyze the touch first. During the delay, if the gesture recognizer recognizes a touch gesture, then the window never delivers the touch object to the view, and also cancels any touch objects it previously sent to the view that were part of that recognized sequence.
For example, if you have a gesture recognizer for a discrete gesture that requires a two-fingered touch, this translates to two separate touch objects. As the touches occur, the touch objects are passed from the app object to the window object for the view where the touches occurred, and the following sequence occurs, as depicted in Figure 1-6.
窗口延遲把觸摸對象傳遞給視圖,這樣手勢識別器就能夠首先分析觸摸。延遲期間,若是手勢識別器識別出一個觸摸手勢,而後窗口就毫不會再把觸摸對象傳遞給視圖,而且還取消任何先前傳遞給視圖的任何觸摸對象,這些觸摸對象都是被識別序列的一部分。好比,若是你有一個手勢識別器用來識別一個離散手勢,該手勢要求一個雙手指的觸摸,該觸摸就會被解釋成兩個單獨的觸摸對象。 當觸摸發生時,觸摸對象從英語程序對象傳遞到觸摸發生視圖的窗口對象,而後發生如下序列,請看圖1-6。
Sequence of messages for touches
圖1-6 觸摸消息序列
The window sends two touch objects in the Began phase—through the touchesBegan:withEvent:
method—to the gesture recognizer. The gesture recognizer doesn’t recognize the gesture yet, so its state is Possible. The window sends these same touches to the view that the gesture recognizer is attached to.
窗口在Began 階段發送兩個觸摸對象---經過 touchesBegan:withEvent: 方法---給手勢識別器。 手勢識別器還不能識別該手勢,所以它的狀態是Possible. 窗口發送這些一樣的觸摸給手勢識別器相關聯的視圖。
2. The window sends two touch objects in the Moved phase—through the touchesMoved:withEvent:
method—to the gesture recognizer. The recognizer still doesn’t detect the gesture, and is still in state Possible. The window then sends these touches to the attached view.
窗口在Moved階段發送兩個觸摸對象---經過touchesMoved:withEvent: 方法---- 給手勢識別器。 識別器任然不能偵測該手勢,狀態仍是Possible。 窗口而後發送這些觸摸到相關聯的視圖。
3.
The window sends one touch object in the Ended phase—through the touchesEnded:withEvent:
method—to the gesture recognizer. This touch object doesn’t yield enough information for the gesture, but the window withholds the object from the attached view.
窗口在Ended階段發送一個觸摸對象--- 經過touchesEnded:withEvent: 方法---給手勢識別器。 雖然該觸摸對象對於手勢來講信息還不夠,可是窗口仍是把該對象扣住(withhold)不發送給視圖。
4.
The window sends the other touch object in the Ended phase. The gesture recognizer now recognizes its gesture, so it sets its state to Recognized. Just before the first action message is sent, the view calls the touchesCancelled:withEvent:
method to invalidate the touch objects previously sent in the Began and Moved phases. The touches in the Ended phase are canceled.
窗口在Ended階段發送另外一個觸摸。 手勢識別器這是能夠識別出該手勢,所以把狀態設置爲Recognized. 就在第一個操做信息被髮送以前,視圖調用touchesCancelled:withEvent: 方法來使先前在Began 和Moved階段發送的觸摸對象無效(invalidate)。觸摸在Ended階段被取消。
Now assume that the gesture recognizer in the last step decides that this multitouch sequence it’s been analyzing is not its gesture. It sets its state toUIGestureRecognizerStateFailed
. Then the window sends the two touch objects in the Ended phase to the attached view in a touchesEnded:withEvent:
message.
如今假設手勢識別器在最後一步肯定它正在分析的多點觸摸序列不是它的手勢。它把狀態設置爲UIGestureRecognizerStateFailed
.。 而後窗口在Ended階段發送這兩個觸摸對象給相關聯的視圖---經過touchesEnded:withEvent: 消息。
A gesture recognizer for a continuous gesture follows a similar sequence, except that it is more likely to recognize its gesture before touch objects reach the Ended phase. Upon recognizing its gesture, it sets its state to UIGestureRecognizerStateBegan
(not Recognized). The window sends all subsequent touch objects in the multitouch sequence to the gesture recognizer but not to the attached view.
一個連續手勢的手勢識別器遵循一個類似的序列,除了它更有可能在觸摸對象到達Ended 階段以前就識別出它的手勢。一旦識別出它的手勢,它把狀態設置爲 UIGestureRecognizerStateBegan (而不是Recognized). 窗口把多點觸摸序列中的全部子序列觸摸對象發送給手勢識別器,而不是發送到附屬的(attached)視圖。
二、影響到視圖的各個觸摸的傳遞
You can change the values of several UIGestureRecognizer
properties to alter the default delivery path in certain ways. If you change the default values of these properties, you get the following differences in behavior:
你能夠改變 UIGestureRecognizer 特性的幾個值來改變默認傳遞路徑,讓它們以特定的方式傳遞。若是你改變這些特性的默認值,如下行爲將發生變化:
delaysTouchesBegan
(default of NO
)—Normally, the window sends touch objects in the Began and Moved phases to the view and the gesture recognizer. Setting delaysTouchesBegan
to YES
prevents the window from delivering touch objects in the Began phase to the view. This ensures that when a gesture recognizer recognizes its gesture, no part of the touch event was delivered to the attached view. Be cautious when setting this property because it can make your interface feel unresponsive.
This setting provides a similar behavior to the delaysContentTouches
property on UIScrollView
; in this case, when scrolling begins soon after the touch begins, subviews of the scroll-view object never receive the touch, so there is no flash of visual feedback.
delaysTouchesEnded
(default of YES
)—When this property is set toYES
, it ensures that a view does not complete an action that the gesture might want to cancel later. When a gesture recognizer is analyzing a touch event, the window does not deliver touch objects in the Ended phase to the attached view. If a gesture recognizer recognizes its gesture, the touch objects are canceled. If the gesture recognizer does not recognize its gesture, the window delivers these objects to the view through a touchesEnded:withEvent:
message. Setting this property to NO
allows the view to analyze touch objects in the Ended phase at the same time as the gesture recognizer.
Consider, for example, that a view has a tap gesture recognizer that requires two taps, and the user double taps the view. With the property set to YES
, the view gets touchesBegan:withEvent:
, touchesBegan:withEvent:
, touchesCancelled:withEvent:
, and touchesCancelled:withEvent:
. If this property is set to NO
, the view gets the following sequence of messages: touchesBegan:withEvent:
, touchesEnded:withEvent:
, touchesBegan:withEvent:
, andtouchesCancelled:withEvent:
, which means that in touchesBegan:withEvent:
, the view can recognize a double tap.
touchesBegan:withEvent:
, touchesBegan:withEvent:
, touchesCancelled:withEvent:
, 以及 touchesCancelled:withEvent: 信息序列。若是該特性被設置爲NO,視圖得到如下信息序列:touchesBegan:withEvent:
, touchesEnded:withEvent:
, touchesBegan:withEvent:
, andtouchesCancelled:withEvent: ,它表示在touchesBegan:withEvent: 消息中,視圖能夠識別一個雙擊。
If a gesture recognizer detects a touch that it determines is not part of its gesture, it can pass the touch directly to its view. To do this, the gesture recognizer callsignoreTouch:forEvent:
on itself, passing in the touch object.
若是一個手勢識別器偵測到一個不屬於該手勢的觸摸,它能夠把該觸摸直接傳遞給它的視圖。 要想實現它,手勢識別器對本身調用ignoreTouch:forEvent:方法,它在觸摸對象中傳遞。
6、建立一個自定義手勢識別器
To implement a custom gesture recognizer, first create a subclass of UIGestureRecognizer
in Xcode. Then, add the following import
directive in your subclass’s header file:
要想實現一個自定義手勢識別器,首先在Xcode裏建立一個 UIGestureRecognizer 的子類。而後,在子類的頭文件中加入如下import指令。
#import <UIKit/UIGestureRecognizerSubclass.h> |
Next, copy the following method declarations from UIGestureRecognizerSubclass.h
to your header file; these are the methods you override in your subclass:
下一步,從UIGestureRecognizerSubclass.h中拷貝如下方法聲明到你的頭文件;這些是你在子類中須要重寫的方法。
- (void)reset; |
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; |
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; |
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; |
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; |
These methods have the same exact signature and behavior as the corresponding touch-event handling methods described earlier in 「An App Receives Touches in the Touch-Handling Methods.」 In all of the methods you override, you must call the superclass implementation, even if the method has a null implementation.
這些方法跟早先在「An App Receives Touches in the Touch-Handling Methods.」 中所描述的相關觸摸事件處理有徹底相同的簽名和行爲。 在全部這些須要重寫的方法中,你必須調用父類的實現,即便該方法有一個null實現。
Notice that the state
property in UIGestureRecognizerSubclass.h
is now readwrite
instead of readonly
, as it is in UIGestureRecognizer.h
. Your subclass changes its state by assigning UIGestureRecognizerState
constants to that property.
注意:UIGestureRecognizerSubclass.h中的 state 特性目前是readwrite狀態,而不是readonly,就跟它在UIGestureRecognizer.h中同樣。 你的子類能夠經過給特性分配
UIGestureRecognizerState 常量(constants)來改變其狀態。
一、爲自定義手勢識別器實現觸摸事件處理方法
The heart of the implementation for a custom gesture recognizer are the four methods: touchesBegan:withEvent:
, touchesMoved:withEvent:
,touchesEnded:withEvent:
, and touchesCancelled:withEvent:
. Within these methods, you translate low-level touch events into gesture recognition by setting a gesture recognizer’s state. Listing 1-8 creates a gesture recognizer for a discrete single-touch checkmark gesture. It records the midpoint of the gesture—the point at which the upstroke begins—so that clients can obtain this value.
一個自定義手勢識別器實現的核心是四個方法: touchesBegan:withEvent:
, touchesMoved:withEvent:
,touchesEnded:withEvent:
, and touchesCancelled:withEvent:
. 在這些方法中,你經過設置一個手勢識別器的狀態,把低層觸摸事件解析爲手勢識別。列表1-8建立了一個離散單擊勾選(checkmark)手勢的手勢識別器。 它記錄了手勢的中心點---即勾的上升開始點---這樣客戶就能夠獲取這個值。
This example has only a single view, but most apps have many views. In general, you should convert touch locations to the screen’s coordinate system so that you can correctly recognize gestures that span multiple views.
該例子只有一個單一視圖,可是大可能是應用程序都有多個視圖。通常來講,你應該把觸摸位置轉換爲屏幕的座標系,這樣你就能夠正確的識別出跨越(span)多個視圖的手勢。
Implementation of a checkmark gesture recognizer
列表1-8 一個勾(checkmark)手勢識別器的實現
#import <UIKit/UIGestureRecognizerSubclass.h> |
// Implemented in your custom subclass |
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { |
[super touchesBegan:touches withEvent:event]; |
if ([touches count] != 1) { |
self.state = UIGestureRecognizerStateFailed; |
return; |
} |
} |
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { |
[super touchesMoved:touches withEvent:event]; |
if (self.state == UIGestureRecognizerStateFailed) return; |
UIWindow *win = [self.view window]; |
CGPoint nowPoint = [touches.anyObject locationInView:win]; |
CGPoint nowPoint = [touches.anyObject locationInView:self.view]; |
CGPoint prevPoint = [touches.anyObject previousLocationInView:self.view]; |
// strokeUp is a property |
if (!self.strokeUp) { |
// On downstroke, both x and y increase in positive direction |
if (nowPoint.x >= prevPoint.x && nowPoint.y >= prevPoint.y) { |
self.midPoint = nowPoint; |
// Upstroke has increasing x value but decreasing y value |
} else if (nowPoint.x >= prevPoint.x && nowPoint.y <= prevPoint.y) { |
self.strokeUp = YES; |
} else { |
self.state = UIGestureRecognizerStateFailed; |
} |
} |
} |
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { |
[super touchesEnded:touches withEvent:event]; |
if ((self.state == UIGestureRecognizerStatePossible) && self.strokeUp) { |
self.state = UIGestureRecognizerStateRecognized; |
} |
} |
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { |
[super touchesCancelled:touches withEvent:event]; |
self.midPoint = CGPointZero; |
self.strokeUp = NO; |
self.state = UIGestureRecognizerStateFailed; |
} |
State transitions for discrete and continuous gestures are different, as described in 「Gesture Recognizers Operate in a Finite State Machine.」 When you create a custom gesture recognizer, you indicate whether it is discrete or continuous by assigning it the relevant states. As an example, the checkmark gesture recognizer in Listing 1-8 never sets the state to Began or Changed, because it’s discrete.
離散手勢和連續手勢的狀態過渡是不同的,正如在 「Gesture Recognizers Operate in a Finite State Machine.」 中所描述。 當你建立一個自定義手勢識別器時,你經過給它分配響應的狀態來代表(indicate)它是離散手勢或是連續手勢。好比,列表1-8 中的複選標記(checkmark)手勢識別器,它不會把狀態設置爲Began 或者 Changed,由於它是離散手勢。
The most important thing you need to do when subclassing a gesture recognizer is to set the gesture recognizer’s state
accurately. iOS needs to know the state of a gesture recognizer in order for gesture recognizers to interact as expected. For example, if you want to permit simultaneous recognition or require a gesture recognizer to fail, iOS needs to understand the current state of your recognizer.
當你子類化一個手勢識別器時,最重要的事情是正確地設置手勢識別器的state。 爲了手勢識別器的的交互如預期,iOS須要瞭解一個手勢識別器的狀態。好比,若是你想要實現同時識別或者要求一個手勢識別器失敗,iOS須要瞭解識別器的當前狀態。
For more about creating custom gesture recognizers, see WWDC 2012: Building Advanced Gesture Recognizers.
關於建立自定義手勢識別器的更多信息,請看 WWDC 2012: Building Advanced Gesture Recognizers.
2. 重置手勢識別器的狀態
If your gesture recognizer transitions to Recognized/Ended, Canceled, or Failed, the UIGestureRecognizer
class calls the reset
method just before the gesture recognizer transitions back to Possible.
若是你的手勢識別器過渡到Recognized/Ended, Canceled, 或者Failed狀態,UIGestureRecognizer 類恰好在手勢識別器過渡回Possible狀態前調用reset 方法。
Implement the reset
method to reset any internal state so that your recognizer is ready for a new attempt at recognizing a gesture, as in Listing 1-9. After a gesture recognizer returns from this method, it receives no further updates for touches that are in progress.
實現reset 方法來重置任何內部狀態,這樣你的識別器能準備進行識別手勢的一個新的嘗試,正如列表1-9所示。當手勢識別器從該方法返回後,它將再也不接收任何在進行的觸摸更新。
Resetting a gesture recognizer
列表1-9 重置一個手勢識別器
- (void)reset { |
[super reset]; |
self.midPoint = CGPointZero; |
self.strokeUp = NO; |
} |