Gesture Recognizers
手势识别器
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.
手势识别把低层次的事件处理代码转换为高层次的操作。 它们是你连接到视图的各种对象,它们允许视图像控件一样响应各种操作。 手势识别翻译(interpret)各种触摸来决定它们是否响应一个特定的手势,比如一个点击(swipe), 捏合(pinch),或旋转。 如果它们识别到它们指定的手势,就给目标对象发送一个操作信息。 目标对象通常是视图的视图控制器,它响应如图1-1中所示的手势。该设计模式既有力又简单;你可以动态地决定视图响应什么操作,你也可以给视图添加各种手势识别而不必要子类化该视图。
Figure 1-1 A gesture recognizer attached to a view
图1-1 一个连接到视图的手势识别
Use Gesture Recognizers to Simplify Event Handling
一、使用手势识别器来简化事件处理
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.
UIKit 框架提供了一些已经预先定义好的手势识别来侦测各种常用手势。如果可能的话,最好是使用预定义的手势识别,因为它们缩减了你必须写的代码总量。另外,使用一个标准的手势识别而不是自己编写以确保你的应用程序如用户期望的那样工作。
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.”
Built-in Gesture Recognizers Recognize Common Gestures
1、内建手势识别来识别各种常用手势
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”。
Gesture Recognizers Are Attached to a View
2、手势识别连接到视图
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.
每个手势识别都跟一个视图相关联。 通常,视图可以有多个手势识别,因为单个视图可能响应多个不同的手势。 要想一个手势识别能够识别在一个特殊视图上发生的各种触摸,你必须把手势识别连接到那个视图。 当用户触摸了那个视图,手势识别先于视图对象接收一个触摸发生的信息。 结果,手势识别可以通过视图的行为来响应各种触摸。
Gestures Trigger Action Messages
3、 手势触发操作消息
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.
当一个手势识别识别出其制定的手势后,它给它的目标发送一个操作消息。 要想创建一个手势识别,你需要初始化一个目标和一个操作。
Discrete and Continuous Gestures
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.
Figure 1-2 Discrete and continuous gestures
图1-2 离散和连续手势
Responding to Events with Gesture Recognizers
二、用手势识别响应事件
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.
实现处理手势的操作方法。
Using Interface Builder to Add a Gesture Recognizer to Your App
1、使用界面生成器(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相似.
Listing 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 |
Adding a Gesture Recognizer Programmatically
2、通过程序添加一个手势识别
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所示。
Listing 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 |
} |
Responding to Discrete Gestures
3、响应离散手势
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 样本代码工程,你可以查看它以获得更多上下文。
Listing 1-3 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 特性来决定用户是向左清扫还是向右。 然后,它使用该值来让图像从点击方向消失。
Listing 1-4 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; |
}]; |
} |
Responding to Continuous Gestures
4、响应连续手势
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: 方法直到两个手指都拿起。
Listing 1-5 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.”
Defining How Gesture Recognizers Interact
三、定义手势识别如何交互
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 Operate in a Finite State Machine
1、手势识别在一个有限状态机里操作
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
).
Figure 1-3 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)状态。把状态重置为可能状态不会触发一个操作消息。
Interacting with Other Gesture Recognizers
2、跟其它手势识别发生交互
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 类方法,委托方法,以及其子类重写的方法来影响这些行为。
Declaring a Specific Order for Two Gesture Recognizers
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所示。在该列表中,两个手势识别都被连接到了相同的视图上。
Listing 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.
如果你想要这两种操作不兼容,你的单击识别必须要求双击识别进入失败状态。 然而,这样你的单击操作会滞后用户的输入,因为单击识别会等到双击识别失败后才开始识别。
Preventing Gesture Recognizers from Analyzing Touches
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: 委托方法来阻止手势识别接收到来自一个自定义子视图中发生的触摸。
Listing 1-7 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:方法 。该方法的签名和实现是一样的。
Permitting Simultaneous Gesture Recognition
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。
Specifying a One-Way Relationship Between Two Gesture Recognizers
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.” 里描述。 默认情况下,捏合手势阻止旋转手势,或者旋转手势阻止捏合手势,因为两个手势不能同时被识别。
Interacting with Other User Interface Controls
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
, andUIPageControl
.- 单个手指在
UIButton
,UISwitch
,UIStepper
,UISegmentedControl
, andUIPageControl 上的
单击。 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.- 单个手指的在UISwitch 控件上的慢速拖动(pan)手势,方向跟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 文档以确保你的应用程序提供了一个直观的用户体验,特别是当你重写一个标准控件的默认行为时。
Gesture Recognizers Interpret Raw Touch Events
四、手势识别解读(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)思考触摸和事件的方方面面。
An Event Contains All the Touches for the Current Multitouch Sequence
1、一个事件包含了单前多点触摸序列的所有触摸
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所示,应用程序在任何触摸的每个阶段之间接收事件对象。
Figure 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.
注意:手指没有鼠标点击精确。 当用户触摸屏幕时,接触的区域实际上是椭圆的,并且会比用户期待的位置稍微篇低。 该接触面会根据手指的尺寸和方向,手指使用时的压力,以及其它因素的不同而发生改变。底层多点触摸系统会替你分析该信息并计算一个单击点,因此你不需要自己写代码来实现它。
An App Receives Touches in the Touch-Handling Methods
2、应用程序在触摸处理方法中接收触摸
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等。 手势识别器状态严格表示手势识别器自身的阶段,不表示正在被识别的触摸对象阶段。
Regulating the Delivery of Touches to Views
五、调节触摸到视图的传递
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对象。 然后,窗口首先把触摸发送给触摸发生的视图上关联的任何手势识别器,而不是先发送给视图对象自身。
Figure 1-5 Default delivery path for touch events
图1-5 触摸事件的默认传递路径
Gesture Recognizers Get the First Opportunity to Recognize a Touch
1、手势识别器首先识别一个触摸
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。
Figure 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)视图。
Affecting the Delivery of Touches to Views
2、影响到视图的各个触摸的传递
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 ofNO
)—Normally, the window sends touch objects in the Began and Moved phases to the view and the gesture recognizer. SettingdelaysTouchesBegan
toYES
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 onUIScrollView
; 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. - delaysTouchesBegan(默认为NO)---正常情况下,窗口在Began 和 Moved 阶段把触摸对象发送给视图和手势识别器。 把delaysTouchesBegan设置为YES,使得窗口不会在Began阶段把触摸对象发送给视图。 这样做确保一个手势识别器识别它的手势时,没有把部分触摸事件传递给相连的视图。 设置该特性时请谨慎,因为它会使你的界面反应迟钝。
-
delaysTouchesEnded
(default ofYES
)—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 atouchesEnded:withEvent:
message. Setting this property toNO
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 getstouchesBegan:withEvent:
,touchesBegan:withEvent:
,touchesCancelled:withEvent:
, andtouchesCancelled:withEvent:
. If this property is set toNO
, the view gets the following sequence of messages:touchesBegan:withEvent:
,touchesEnded:withEvent:
,touchesBegan:withEvent:
, andtouchesCancelled:withEvent:
, which means that intouchesBegan:withEvent:
, the view can recognize a double tap. - delaysTouchesEnded(默认为YES)---当该特性被设置为YES时,它确保视图不会完成一个动作,而该动作是手势可能想在稍候取消的。当一个手势识别器正在分析一个触摸事件时,窗口不会不会在Ended阶段传递触摸对象到相连的视图。如果一个手势识别器识别出它的手势,则触摸对象被取消。 如果手势识别器没有识别出它的手势,窗口通过一个touchesEnded:withEvent:消息把这些对象传递给视图。设置该特性为NO,允许视图和手势识别器可以同时在Ended阶段分析触摸对象。
例如,设想一个视图有一个点击手势识别器,它要求双击,然后用户双击了该视图。 把特性设置为YES后,视图获得
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:方法,它在触摸对象中传递。
Creating a Custom Gesture Recognizer
六、创建一个自定义手势识别器
To implement a custom gesture recognizer, first create a subclassof 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)来改变其状态。
Implementing the Touch-Event Handling Methods for a Custom Gesture Recognizer
1、为自定义手势识别器实现触摸事件处理方法
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)多个视图的手势。
Listing 1-8 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.
Resetting a Gesture Recognizer’s State
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所示。当手势识别器从该方法返回后,它将不再接收任何在进行的触摸更新。
Listing 1-9 Resetting a gesture recognizer
列表1-9 重置一个手势识别器
- (void)reset { |
[super reset]; |
self.midPoint = CGPointZero; |
self.strokeUp = NO; |
} |