<Event Handling Guide for iOS> 读书笔记总结

来源:互联网 发布:java线程停止的方法 编辑:程序博客网 时间:2024/05/08 03:55

前两天静下心来在公交车上把iOS中的touch事件处理过了一遍,包括<View Programming Guide for iOS>和<Event Handling Guide for iOS>两本书,这两天要开始把所有的笔记总结记录一遍,终于对iOS中的事件分发和处理机制有了一个大概的了解,我的理解中,iOS通过hit-testing来进行touch事件的分发,然后通过手势和touch事件处理回调来进行处理。


EventHandlingiPhoneOS
Responding to Events with Gesture Recognizers
2014-12-22 15:10:01
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 targeta single action message. A gesture recognizer for continuous gestures keeps sending action messages to itstarget until the multitouch sequence ends
手势分为分离性的和连续性的两种
Gesture Recognizers Operate in a Finite State Machine
2014-12-22 15:15:29
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.
Declaring a Specific Order for Two Gesture Recognizers
2014-12-22 16:12:26
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:
● Specify that one gesture recognizer should analyze a touch before another gesture recognizer.
● All two gesture recognizers to operation simultaneously
● Prevent a gesture recognizerfrom analyzing a touch

默认情况下,同一个view的多种手势的触发是无序的,但是可以通过API来实现有序控制;
● 可以指定一个手势在另外一个手势之前进行touch事件的解析
● 两个手势可以在同一时间内操作
● 不让一个手势进行touch事件的解析

Preventing Gesture Recognizers from Analyzing Touches
2014-12-22 16:14:38
The requireGestureRecognizerToFail: method sends a message to the receiver and specifies someotherGestureRecognizer that must fail before the receiving recognizer can begin
一个手势调用这个方法,确保另外一个手势失败之后才会被调用,通过这个接口可以控制手势的一定顺序
2014-12-22 16:16:14
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
一般来说,当应用中同时有单击和双击事件的时候,而你并没有设置触发顺序,这个时候单击手势是无法被响应的。而且这种行为在设计用户交互的时候,应该尽量避免。如果你设置单击事件在双击事件失败之后被触发,那么单击事件响应只会在双击事件失败之后才会被调用,这个时候单击时间会有一些延迟才会被调用。
2015-01-11 08:37:29
If you need to wait as long as possible before deciding whether or not a gesture recognizer should analyze atouch,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 recognize
可以使用手势的delegate方法来灵活控制手势是否可以响应事件
Interacting with Other User Interface Controls
2015-01-11 08:37:39
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 the gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
 method, an optional method of the UIGestureRecognizerDelegate protocol

默认情况下,两个手势是无法同时被触发的,只有通过实现UIGestureRecognizerDelegate的方法gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:才能控制
2014-12-22 16:27:00
You need to implement a delegate and return YES on only one of your gesture recognizersto allow simultaneous recognition. However, that also means that returning NO doesn’t necessarily prevent simultaneous recognition because the other gesture recognizers delegate could return YES.
只需要在一个手势的delegate方法中返回yes即可实现两个手势同时被触发,这样就是说即使一个手势这个delegate的同时调用的方法返回NO,这个手势依然有可能是同时被触发的,只有其他手势返回YES就行
2015-01-11 08:37:50
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 the canPreventGestureRecognizer: or canBePreventedByGestureRecognizer: subclass methods to return NO (default is YES)
2014-12-22 16:35:45
In iOS 6.0 and later, default control actions prevent overlapping gesture recognizer behavior. This applies only to gesture recognition that overlaps the default action for a control, which includes:
A single finger single tap on a UIButton, UISwitch, UIStepper, UISegmentedControl, andUIPageControl
A single finger swipe on the knob of a UISlider, in a direction parallel to the slider.
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 agesture recognizer directly to the control instead of to the parent view. Then, the gesture recognizer receives the touch event first.
这里的意思是在UIControl的parent view上面添加特定的手势的时候(上面列表中UIControl与手势的对应关系),parent view上面相应的手势不会被触发的,对应的UIControl会拦截这些touch事件。但是如果把对应的手势直接添加到对应的UIControl上面,这个时候就是手势会首先接收到touch事件。
Gesture Recognizers Get the First Opportunity to Recognize a Touch
2014-12-22 16:46:47
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
2015-01-11 08:38:34
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.
这里的概念值得注意,手势和view自身点击事件的处理顺序,touch事件会首先传递给手势,如果手势被成功解析,window就不会再把touch事件传递给view,并且已经传递的touch 对象也会被cancel。
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 to UIGestureRecognizerStateFailed. Then the window sends the two touch objects in the Ended phase to the attached view in a touchesEnded:withEvent: message.
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 objectsin the multitouch sequence to the gesture recognizer but not to the attached view
默认情况下,只有手势解析失败,view的touch end回调才会被调用,否则调用touch cancel回调,也就是默认情况下,手势的解析高于view的touch的回调。即使对于添加在parent view中的手势也是如此,即如果parent view中的手势解析成功,sub view的touch end依旧不会被执行,只会执行touch cancel。而且对于连续手势,只要手势通过一些早期的touch事件就可以被识别之后,后续的touch事件再也不会被发送到view的touch回调中,touch事件会全部被手势给拦截。
2014-12-22 17:53:26
You can change the values of several UIGestureRecognizer properties o alert the default delivery path in certain ways. If you change the default values of these properties, you get the following differences in behavior:
● 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 neverreceive the touch, so there is no flash of visual feedback.
●  delaysTouchesEnded  (default of YES)—When this property is set to YES, 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.
这和上面的属性决定着手势和view的touch回调的调用时间点以及回调方法的执行
Implementing the Touch-Event Handling Methods for a Custom Gesture Recognizer
2014-12-22 17:56:42
In all of themethods you override, you must call the superclass implementation, even if the method has a null implementation
自定义手势,需要注意的点一,要把所有的touch处理回调都实现!即使里面没有任何内容
Resetting a Gesture Recognizer’s State
2015-01-11 08:39:10
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
注自定义手势,需要注意的点二
2015-01-11 08:39:06
implement the reset method to reset any internal state so that your recognizer is ready for a new attempt at recognizing a gesture
自定义手势,需要注意的点三
Event Delivery: The Responder Chain
2014-12-23 14:12:41
First, the singleton UIApplication object takes an event from the top of the queue and dispatches it for handling. Typically, it sends the event to the app’s key window object, which passes the event to an initial object for handling. The initial object depends on the type of event.
● Touch events. For touch events, the window object first tries to deliver the event to the view where the touch occurred. That view is known as the hit-test view.
● Motion and remote control events. With these events, the window object sends the shaking-motion or remote control event to the first responder for handling.


Hit-Testing Returns the View Where a Touch Occurred
2015-01-11 08:39:26
iOS uses hit-testing to find the view that is under a touch. Hit-testing involves checking whether a touch is within the bounds of any relevant view objects. If it is, it recursively checks all of that view’s subviews. The lowest view in the view hierarchy that contains the touch point becomes the hit-test view. After iOS determines the hit-test view, it passes the touch event to that view for handling.
hit-testing 某种程度上类似Android中的Touch事件分发机制,是iOS中对Touch事件进行分发的机制,会影响到后续Touch事件的处理
2015-01-11 08:39:32
The hitTest:withEvent: method returns the hit test view for a given CGPoint and UIEvent. The hitTest:withEvent:
method begins by calling the pointInside:withEvent: method on itself. If the point passed into hitTest:withEvent: is inside the bounds of the view, pointInside:withEvent: returns YES. Then, the method recursively calls hitTest:withEvent: on every subview that returns YES.
If the point passed into hitTest:withEvent: is not inside the bounds of the view, the first call to the pointInside:withEvent:
method returns NO, the point is ignored, and hitTest:withEvent: returns nil. If a subview returns NO, that whole branch of the view hierarchy is ignored, because if the touch did not occur in that subview, it also did not occur in any of that subview’s subviews. This means that any point in a subview that is outside of its superview can’t receive touch events because the touch point has to be within the bounds of the superview and the subview. This can occur if the subview’s clipsToBounds property isset to NO
View的分发主要是通过hitTest:withEvent来进行的,通过这个方法返回的view对象来确定哪个View第一个来处理touch事件,然后touch事件会在这个View的responder chain中进行传递。而调用hitTest:withEvent的时候会首先调用自身的 pointInside:withEvent方法,如果返回YES,表示点在这个View的bound范围内,返回NO表示则是直接会忽略掉这个View,表示这个View不会处理这个touch事件。返回YES的时候,这个view如果有subview的话,会递归调用subview的hitTest:withEvent方法来获取需要返回的View;如果没有subview,则直接返回这个view。值得注意的是,如果view的 clipsToBounds 属性设为NO的话,即使这个view的subview显示在这个view的bounds之外,这个subview也是无法接收到这个点击事件的。
The Responder Chain Is Made Up of Responder Objects
2014-12-23 14:30:47
The responder chain is a series of linked responder objects. It starts with the first responder and ends with the application object. If the first responder can not handle an event, it forwards the event to the next responder in the responder chain.
2014-12-23 14:31:27
Instances of the UIApplication, UIViewController, and UIView classes are responders, which means that all views and most key controller objects are responders. Note that CoreAnimation layers are not responders
UIApplication,UIViewController, UIView的实例都是responder,这意味着所有的view和大多数重要的controller对象都是responders,但是值得注意的是Core Animation layer不是responder
2014-12-23 14:34:35
Make sure that your app has established its object graph before assigning an object to bethe first responder.
The Responder Chain Follows a Specific Delivery Path
2014-12-23 14:38:53
If you implement a custom view to handle remote control events, action messages, shake-motionevents with UIKit, or editing-menu messages, don’t forward the event or message to next Responder directly to send it up the responder chain. Instead, invoke the superclass implementation of the current event handling method and let UIKit handle the traversal of the responder chain for you.
注意,不能自己来随便更改responder chain,尽量通过superclass的实现来处理这些event

Implementing the Touch-Event Handling Methods in Your Subclass
2014-12-24 18:02:34
a set of touches and an event
2014-12-24 18:04:27
This differs from the set of touches becausesome of the touch objects in the event may not have changed since the previous event message
2014-12-24 18:11:38
In all methods, be sure to call the superclass implementation of the method
Retrieving and Querying Touch Objects
2014-12-24 18:25:19
The multipleTouchEnabled property is set to No by default, which means that a view receives only the first touch in a multitouch sequence. When this property is disabled, you can retrieve a touch object by calling the any Object
method on the set object because there is only one object in the set
2014-12-24 18:26:46
you are interested only in touches associated with a specific window, call the touchesForWindow: method of the UIEvent object
2014-12-24 18:26:55
you want to get the touches associated with a specific view, call the touchesForView: method of the event object.
上面是几种获取Touch对象的方法,如果是只有 multipleTouchEnabled 属性设置为NO的话,view只能获取到第一个touch,这个时候可以使用set的 anyObject 方法来获取一个UITouch对象;至于 touchesForWindow 和 touchesForView方法是两个筛选方法。

Handling a Complex Multitouch Sequence
2014-12-24 18:59:17
To track and handle multiple touches,  you need to:
●  set the view’s multipleTouchEnabled property to YES
●  Use a Core Foundation dictionary object (CFDictionaryRef) to track the mutations of touches through their phases during the event
When handling an event with multiple touches, you often store information about a touch’s state so that you can compare touches later. Use a CFDictionaryRef data type rather than an NSDictionary object to track touches, because NSDictionary copies its keys. The UITouch class does not adopt the NSCopying protocol, which is required for object copying.
使用CFDictionaryRef来存储touch数据,是因为NSDictionary是需要对key进行copy的,但是UITouch对象并没有实现NSCopying协议,所以不能使用NSDictionary来存储UITouch。至于具体的实现代码,看官方文档

2015-01-11 08:40:05
To find out when the last finger in a multitouch sequence is lifted from a view, see how many touch objects are in the passed-in set and how many are in the passed-in UIEvent object. If the number is the same, then the multitouch sequence has concluded
2014-12-25 10:15:27
Remember that a passed-in set contains all touch objects associated with the view that are new or changed for a given phase, whereas the touch objects returned from the touchesForView: method includes all objects associated with the specified view.

Specifying Custom Touch Event Behavior

Customize the way your app handles touches by alerting the behavior of a specific gesture, a specific view, or all of touch events in your app. You can alert the stream of touch events in the following ways:
●  Turn on delivery of multiple touches
●  Restrict event delivery to a single view. By default, a view’s exclusiveTouch property is set to NO, which means that one view does not block other views in a window from receiving touches. If you set the property to YES for a specific view, then that view receives touches if—and only if—it is the only view tracking touches
●  Restrict event delivery to subviews. A custom UIView class can override hitTest:withEvent: so that multitouch events are not delivered to a specific subviews.
exclusiveTouch属性是一个很有意思的属性,如果一个View这个属性设置为YES的时候,当它和其他view一起被touch的话,只有那个最先被touch的view才可以接收到touch事件。

2014-12-25 10:51:39
You can also turn off touch-event delivery completely, or just for a period of time:
●  Turn off delivery of touch events. Set a view’s userInteractionEnabled property to NO to turn off delivery of touch events. Note that a view also does not receive touch events if it’s hidden or transparent.
●  Turn off delivery of touch events for a period. Sometimes you want to temporarily turn off event delivery  for example, while your code is performing animations. Your app can call the beginIgnoringInteractionEvents method to stop receiving touch events, and then later call the endIgnoringInteractionEvents method to resume touch-event delivery.
使用userInteractionEnabled属性关闭View所有的touch事件,注意这个是可以根据需求设置的,比如某个时期内这个view可以响应touch事件,但另外一种情况下这个View不可以响应touch事件,这个可以通过userInteractionEnabled属性很容易实现。另外一点,当一个view的userInteractionEnabled设置为NO的时候,它的subviews也是无法响应事件的,比如UIImageView中添加的UIButton是无法被点击的,除非设置UIImageView的userInteractionEnabled为YES。

2014-12-25 14:00:26
If you have a custom view with subviews, you need to determine whether you want to handle touches at the subview level or the superview level. If you chose to handle touches at the superview level—meaning that your subviews do not implement the touchesBegan:withEvent:,  touchesEnded:withEvent:,  or touchesMoved:withEvent:
 methods —then the superview should override hitTest:withEvent: to return itself rather than any of its subviews.
Overriding hit-testing ensures that the superview receives all touches because, by setting itself as the hit-test view, the superview intercepts and receives touches that are normally passed to the subview first. If a superview does not override hitTest:withEvent:, touch events are associated with the subviews where they first occurred and never sent to the superview.
如果subview没有设置userInteractionEnabled为NO,并且实现了touch事件处理(touchesBegan:withEvent:等)的方法,那么superview就将无法接收到touch事件处理的方法。但是这个地方需要注意的是,这个时候superview上面的手势仍然是可以响应的(见上文中view的touch处理方法和手势的调用优先级的说明),其实我自己观察的结果是,superview上面的手势和直接添加在subview上面的手势来说是一样的调用顺序,虽然subview上面的touch处理方法已经实现,但是无法影响手势,说明这是两套事件处理程序,之间是有关联的。如果这个时候,superview要进行touch事件处理,那么这个时候superview干脆重写hitTest:withEvent,不返回对应的subview,直接返回superview,这个时候,superview就会成为第一个处理touch事件的view,也就不存在。


Forwarding Touch Events
2014-12-25 14:09:24
To forward touch events to another responder object, send the appropriate touch-event handling messages to that object. Use this technique with caution because UIKit classes are not designed to receive touches that are not bound to them. For a responder object to handle a touch, the touch’s view property must hold a reference to the responder. If you want to conditionally forward touches to other responders in your app, all of the responders should be instances of your own UIView subclass.
2015-01-11 08:40:22
Event forwarding often requires analyzing touch objects to determine where they should be forwarded. There are a couple of approaches you can take for this analysis:
●  With an “overlay” view, such as a common superview, use hit-testing to intercept events for analysis priorto forwarding them to subviews
●  Override sendEvent: in a custom subclass of UIWindow, analyze touches, and forward them to the appropriate responders
Best Practices for Handling Multitouch Events
2014-12-25 14:23:01
When handling both touch and motion events, there are a few recommended techniques and patterns you should follow:
●  Always implement the event cancelationmethods.
          In your implementation, restore the state of a view to what it was before the current multitouch sequence. If you don’t, your view could be left in an inconsistent state, or in some cases, another view could receive the cancelation message.

●  If you handle events in a subclass of UIView, UIViewController, or UIResponder:
          ●  Implement all of the event handling methods, even if your implementations of those methods do nothing. [即使不需要,也必须把所有的事件处理方法实现,即使里面是空的]
          ●  Do not call the superclass implementation of the methods.  [不要调用super中对应的实现]

●  If you handle events in a subclass of any other UIKit responder class: 
          ●  You do not have to implement all of the event handling methods.
          ●  In the methods you do implement, be sure to call the super class implementation. For example, [super touchesBegan:touches withEvent:event]

●  Do not forward events to other responder objects of the UIKit framework.
          Instead, forward events to instances of your own subclasses of UIView. Additionally, make sure these responder objects are aware that event forwarding is taking place and that they can receive touches thatare not bound to them.

●  Do not explicitly send events up the responder chain through the nextResponder method; instead, invoke the superclass implementation and let UIKit handle responder-chain traversal.

● Do not use round-to-integer code in your touch handling because you lose precision.
          iOS reports touches in a 320x480 coordinate space to maintain source compatibility. However, on high-resolution devices, the resolution is actually twice as high in each dimension(640x960), which means that touches can land on half-point boundaries on high-resolution devices. On older devices, touches land only on full-point boundaries.



多看笔记 来自多看阅读 for iOS
duokanbookid:o23111209140g496b614g920597e0db1

0 0
原创粉丝点击