|
管理响应链的方法nextResponder Returns the receiver'€™s next responder, or nil if it has none. 接受者的下一个响应者,如果没有的话就为 nil。nextResponder 就是响应链中下一个处理事件的对象。 UIResponder 类不会自动存储 nextResponder,所以默认返回 nil。子类化的时候需要重载该方法自己设置。一般情况下,UIView 一般返回它所在的 UIViewController 或者它的 superview;而 UIViewController 返回它 view 的 superview 或者 UIViewController (会一直循环找,直到找到 UIWindow -> UIApplication);UIWindow 返回 UIApplication;UIApplication 返回 nil。所以,响应链在视图层级构建的时候就已经形成了。 isFirstResponderReturns a Boolean value indicating whether the receiver is the first responder. 是否是第一响应者。默认为 YES。 canBecomeFirstResponderReturns a Boolean value indicating whether the receiver can become first responder. 是否能够成为第一响应者。默认为 NO。 如果返回 YES ,就说明它能够成为第一响应者,it becomes the first responder and can receive touch events and action messages.能够接受触摸事件和动作消息。子类如果想要成为第一响应者,那么必须重载这个方法。注意,你只有当 view 已经添加到 view 层级里面才能发送这个消息(becomeFirstResponder),不然这个结果是不确定的,例子如下: Note: Make sure that your app has established its object graph before assigning an object to be the first responder. For example, you typically call the becomeFirstResponder method in an override of the viewDidAppear: method. If you try to assign the first responder in viewWillAppear:, your object graph is not yet established, so the becomeFirstResponder method returns NO.* A: T Y1 V; x" U" b- R' v6 }# b
注意:在一个对象成为 first repsonder 之前要确保建立好 object graph。例如,你通常在 viewDidAppear: 方法里面调用 becomeFirstResponder。如果 viewWillAppear: 方法里面设置 first responder,这个时候 object graph 还没建立好,所以 becomeFirstResponder 会返回 NO。 becomeFirstResponderNotifies the receiver that it is about to become first responder in its window. 报告接受者它将要在 window 上成为为第一响应者。默认返回 YES。 A responder object only becomes the first responder if the current responder can resign first-responder status (canResignFirstResponder) and the new responder can become first responder. 只有当前的响应者能够辞去第一响应者,新的响应者才能够成为第一响应者。也就是说第一响应者永远只有一个。 If the view’s window property holds a UIWindow object, it has been installed in a view hierarchy;if it returns nil, the view is detached from any hierarchy. view 的 window 属性持有 UIWindow 对象时才表示这个 view 已经添加到 view 层级中。也就说只有 view 层级确定成功后才能成为第一响应者。 canResignFirstResponderReturns a Boolean value indicating whether the receiver is willing to relinquish first-responder status.是否能够将要放弃作为第一响应者的状态。默认为 YES。 As an example, a text field in the middle of editing might want to implement this method to return NO to keep itself active during editing. 例如,编辑中的文本输入框可能想实现这个方法返回 NO 来保持自己的编辑状态(不过,这种情况目前还没有遇到过。)。 resignFirstResponderNotifies the receiver that it has been asked to relinquish its status as first responder in its window.通知接受者它被询问是否放弃在 window 上作为第一响应者的状态。 默认为 YES。 注意:子类重载该方法的时候,必须实现父类的方法。 简单实例点击某个 view 出现 copy 等菜单的 UIMenuController 时,我们会重载 canBecomeFirstResponder 方法并返回 YES; UIApplicationA major role of your app’s application object is to handle the initial routing of incoming user events. It dispatches action messages forwarded to it by control objects (instances of the UIControl class) to appropriate target objects. application 主要的职责是处理用户事件。 sendEvent:- g( A- l8 l& L) I2 x8 i2 u) Y
分发一个消息给合适的响应者对象。你可以子类 UIApplication 对象并且重载这个方法来拦截事件。但是拦截完后记得调用父类的实现 [super sendEvent:event]。 sendAction:to:from:forEvent:
7 p9 J3 ?, u. P; s% r转发消息给特定的对象。 C0 t* W# l4 G: C: ^& q' U
target:接受消息的对象,如果为 nil,那么 APP 会发送给第一响应者,然后沿着响应链传递。$ E$ A) P! C7 m. ~! _" t9 d
sender:发送 action 消息的对象。默认的 sender 是 UIControl 对象。 UIControlThe UIControl class implements common behavior for visual elements that convey a specific action or intention in response to user interactions. UIControl 为响应用户的交互而对那些可见的元素实现了共同的行为,其实也是事件的高级处理。它使用来 Target-Action 机制向 APP 报告用户的交互。 UIControl 由 UIControlState 类型的属性 state 决定它的外观和支持用户交互的能力。 The control handles all the work of tracking incoming touch events and determining when to call your methods.处理所有的跟踪将要来的触摸事件的工作,并且决定什么时候调用你的方法。通过 addTarget:action:forControlEvents: 方法添加 target 和 action ,target 可以为任何对象,一般是包含 control 的 view controller,如果 target 为 nil,那么控件会通过响应链查找定义了该方法的响应者。 The control maintains a list of its attached targets and actions along and the events each supports.里面维持了一个数组来存储它的 target、action 已经所支持的事件。control 不会 retain target。可以参考iOS-Runtime-Headers _targetActions数组 用 Xcode 在 UIControl 的响应事件里面断点可以看到 _targetActions 数组
+ Q8 h4 k w7 Y$ G* R# c - sendActionsForControlEvents:
复制代码 3 S- x5 f; V. ]9 S4 T
This method iterates over the control’s registered targets and action methods and calls the sendAction:to:forEvent: method for each one that is associated with an event in the controlEvents parameter. 从 iterates 可以看出 UIControl 里面是维持了一个数组。 响应方法有三种形式
% ?6 t8 c- V$ B. o0 p7 A - - (IBAction)doSomething;
4 B: e" ~5 |# G8 A5 c+ O- V - - (IBAction)doSomething:(id)sender;: _6 U' b$ G: }4 }" w2 l4 o0 U) e, x( F
- - (IBAction)doSomething:(id)sender forEvent:(UIEvent*)event; // sender就是调用这个方法的对象,一般就是control自己;而event就是触发这个control的相关事件
复制代码 # `+ c( s2 R* {- |5 N# P* k
3 W t- V# i) k/ `/ t$ V1 H4 X1 \' P根据 UIControlEvents 来指定用户交互的特定形式,例如:UIButton 就是 UIControlEventTouchDown 或者 UIControlEventTouchUpInside 触发 action 方法,而 UISlider,则是 UIControlEventValueChanged。" w' e4 k7 w* g
When a control-specific event occurs, the control calls any associated action methods right away. Action methods are dispatched through the current UIApplication object, which finds an appropriate object to handle the message, following the responder chain if needed. For more information about responders and the responder chain, see Event Handling Guide for iOS. 当一个特定的事件发生时,control 就正确的调用相关的 action 方法。通过 UIApplication 对象(它能够找到相应的对象来处理消息)来分发 action 方法,如果需要的话,则通过响应链来找到。 子类化 UIControl 使你能够简单支持事件处理。用下面两种方法中的一种来改变它的行为。 - 重载 sendAction:to:forEvent: 方法,观察或者修改分发 action 方法到相关的 target;
- 重载 beginTrackingWithTouch:withEvent:, continueTrackingWithTouch:withEvent:, endTrackingWithTouch:withEvent:, cancelTrackingWithEvent: 方法当事件发生时,跟踪它们。用这些方法代替 UIResponder 定义的 touchXxx:withEvent: 方法;
% J: l# F$ }7 O2 _3 e- ~7 q
调用一个特定的方法。这个方法带着提供的信息并且将它转发给单例 UIApplication 去分发。' }. q- A/ r" Z& w; @! \
- beginTrackingWithTouch:withEvent:
复制代码
3 N+ m- W# n* Q# E当 touch 事件发生在控件里面时会调用这个方法。 Target-ActionTarget-action is a design pattern in which an object holds the information necessary to send a message to another object when an event occurs. Target-action
" w3 V- O! d d' Q" W( j& n5 o, k7 }是一种设计模式,当某个事件发生时,持有信息的对象发送消息给另外一个对象。持有的信息包括接受消息的对象以及消息。 上面图片所表示的可以用下面的代码表示:0 T3 J& e6 j/ v* ?) _* N/ h1 c
- // viewcontroller9 l4 D) x9 G% j" K: e1 E9 O: d! u
- - (void)viewDidLoad {, V: O6 {# Q! w O9 q% G8 R7 a) N
- [super viewDidLoad];' W% \1 K8 S/ H. }6 e
- UIControl *control = [[UIControl alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
3 j* P1 W7 @- R5 W$ K- L/ Y - [control addTarget:self action:@selector(restoreDefaults:) forControlEvents:UIControlEventTouchUpInside];
' d4 L+ O- L6 H/ K - [self.view addSubview:control]; o) K4 U0 f5 D" M' r5 |# `
- }$ A# S! H4 z8 ]
- - (void)restoreDefaults:(id)sender {2 b. m1 g0 d" T- P
- 2 H3 s( P6 }3 d' ~
- }
复制代码 3 g6 t; s4 k7 ~- C! ~; ]$ B1 \
即 UIControlEventTouchUpInside 类型的事件发生时,事件会传递到 control 对象,然后由 control 去触发 target 的 action 行为。UIGestureRecognizer 也是类似的。 Gesture Recognizers上面有提到 Gesture recognizers convert low-level event handling code into higher-level actions. UIGestureRecognizer is an abstract base class for concrete gesture-recognizer classes. A gesture recognizer doesn’t participate in the view’s responder chain. 尽管它是添加在 view 上的,但是它不参与 view 的响应链。 那来看看 Gesture Recognizers 是怎么个高级法? 当它添加到 view 上时,它能够让 view 像 control 一样响应特定的事件。 内置手势以及其用法系统已经帮我们内置几个非常实用的手势: - UITapGestureRecognizer:Tapping (any number of taps)。点击手势,例如:单击、双击、三击。
- UIPinchGestureRecognizer
inching in and out (for zooming a view)。缩放手势,例如:相册放大缩小。 - UIPanGestureRecognizer
anning or dragging。拖拽手势,例如:scrollview 的拖动滚动。 - UISwipeGestureRecognizer:Swiping (in any direction)。滑动手势,例如:浏览相册。
- UIRotationGestureRecognizer:Rotating (fingers moving in opposite directions)。旋转手势,例如:两个手指旋转相册的照片。
- UILongPressGestureRecognizer
ong press (also known as “touch and hold”)。长按手势,例如:朋友圈长按文本出现复制等菜单。 - UIScreenEdgePanGestureRecognizer: swipe up from the bottom of the screen to reveal Control Center。从屏幕下面从下往上滑动,出现控制中心。
/ ?; s% L2 i; `5 e4 a8 w: p3 m9 U
每个 gesture recognizer 都跟一个 view 相关联,所以它得添加到 view 上。一个 view 可以有多个 gesture recognizer,通过gestureRecognizers属性来获取。 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. 当用户触摸 view 的时候,gesture recognizer 会在 view(靠 touchBegan、moved、ended、cancelled:withEvent:这几个方法来处理 touch 事件)之前收到这个 touch 事件。所以 gesture recognizer 能够代表 view 来响应这个 touch。 gesture recognizer 分为离散的和连续的。从下图可以看出,离散的只会发送一次action message给 target ,而连续的则会发送多次直到这个触摸队列完成。 - 离散的有:UITapGestureRecognizer、UISwipeGestureRecognizer;
- 连续的有:UIPinchGestureRecognizer、UIPanGestureRecognizer、UIRotationGestureRecognizer、UILongPressGestureRecognizer;
- A1 R. v+ G( D7 n3 i
使用手势的方法也很简单: - - (void)viewDidLoad {
, b/ ~3 p2 n6 S5 V% U# y - [super viewDidLoad];
9 }8 l( K6 k y, q -
) O7 G" d* Q' |* i" o1 ]% W - // Create and initialize a tap gesture7 n/ G' t. ~$ ^8 V1 ~& V4 l0 E
- UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
4 c: ] F! w4 g9 O7 e2 G* D - initWithTarget:self action:@selector(respondToTapGesture:)];" y; y! d& m Z
-
, b1 u/ h: a0 a - // Specify that the gesture must be a single tap
4 K0 Y/ n+ d: H/ H7 V) D - tapRecognizer.numberOfTapsRequired = 1;
2 d o, d( _& ~* Y1 n -
. u) B, ~: g( C" l- f - // Add the tap gesture recognizer to the view- j& I+ l5 V1 R3 |
- [self.view addGestureRecognizer:tapRecognizer];
- `6 A7 @* N/ }3 \. V. _ - }2 B/ j, ^8 V( b: h' O; ^. Z% a
- // Respond to a rotation gesture 离散的手势需要在响应方法里面判断它的状态! }$ N! w/ T$ F
- - (IBAction)showGestureForRotationRecognizer:(UIRotationGestureRecognizer *)recognizer {: s# a# \2 }) }
- if (([recognizer state] == UIGestureRecognizerStateEnded) || ([recognizer state] == UIGestureRecognizerStateCancelled)) {; z( K8 w7 `9 {. S6 r Z
- [UIView animateWithDuration:0.5 animations:^{0 o3 M0 m7 c% w0 g/ a
- self.imageView.alpha = 0.0;
" ]: h4 }8 G+ W, S, c - self.imageView.transform = CGAffineTransformIdentity;. `* T: M3 u7 o
- }];
" S3 U; v2 ^$ t* Y' G7 L+ T, N - }
4 O+ V: h2 B; R" q/ l# X. y( Z& l - }
复制代码
0 T+ a+ M( O' s! J2 Z; ]( O7 `$ g" N/ I |