根据iOS文档,响应者链用于“向上传递”触摸事件。它也用于控件生成的操作。细
我真正想做的是在链上发送自定义事件。接收事件的第一响应者将处理它。这似乎是一种非常常见的模式,但我找不到任何关于如何使用“iOS / Cocoa方式”的好解释。
由于响应者链正是我所需要的,我想出了这样的解决方案:
// some event happened in my view that
// I want to turn into a custom event and pass it "up":
UIResponder *responder = [self nextResponder];
while (responder) {
if ([responder conformsToProtocol:@protocol(ItemSelectedDelegate)]) {
[responder itemSelected:someItem];
break;
}
responder = [responder nextResponder];
}
这很有效,但我觉得应该有其他方法来解决这个问题。这种方式手动走链似乎不太好......
请注意,通知在这里不是一个好的解决方案,因为我只希望参与视图层次结构中的对象,并且通知是全局的。
在iOS中处理这个问题的最佳方式是什么(和Cocoa一样)?
修改:
我想要完成什么?
我有一个视图控制器,它有一个视图,它有子视图等...几个子视图是一个特定类型,显示数据库中的项目。当用户点击此视图时,应将信号发送到控制器以导航到此项目的详细信息页面。
处理点按的视图位于视图层次结构中主视图下方的几个级别。我必须告诉控制器(或者在某些情况下,特定的子视图“在链上”)选择了一个项目。
听取通知是一种选择,但我不喜欢这种解决方案,因为选择一个项目不是全局事件。它严格依赖于当前的视图控制器。
答案 0 :(得分:13)
UIApplication has a method for just this purpose和its Cocoa cousin一样。您可以使用一条消息替换问题中的所有代码。
答案 1 :(得分:12)
你非常接近。更标准的是这样的事情:
@implementation NSResponder (MyViewController)
- (void)itemSelected:(id)someItem
{
[[self nextResponder] itemSelected:someItem];
}
@end
默认情况下,事件通常是如何将事件传递给链。然后在右侧控制器中,覆盖该方法,而不是采取自定义操作。
这可能不是您想要实现的目标的正确模式,但它是将消息传递到响应者链的好方法。
答案 2 :(得分:4)
如果你确定第一响应者设置正确,那么彼得的解决方案就有效。如果您希望更多地控制通知哪些对象的事件,则应使用targetForAction:withSender:代替。
这允许您指定应该能够处理事件的第一个视图,并从那里它将爬行响应者链,直到找到可以处理该消息的对象。
以下是您可以使用的fully documented功能:
@interface ABCResponderChainHelper
/*!
Sends an action message identified by selector to a specified target's responder chain.
@param action
A selector identifying an action method. See the remarks for information on the permitted selector forms.
@param target
The object to receive the action message. If @p target cannot invoke the action, it passes the request up the responder chain.
@param sender
The object that is sending the action message.
@param userInfo
The user info for the action. This parameter may be @c nil.
@return
@c YES if a responder object handled the action message, @c NO if no object in the responder chain handled the message.
@remarks
This method pushes two parameters when calling the target. This design enables the action selector to be one of the following:
@code
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender userInfo:(id)userInfo@endcode
*/
+ (BOOL)sendResponderChainAction:(SEL)action to:(UIResponder *)target from:(id)sender withUserInfo:(id)userInfo;
@end
实现:
@implementation ABCResponderChainHelper
+ (BOOL)sendResponderChainAction:(SEL)action to:(UIResponder *)target from:(id)sender withUserInfo:(id)userInfo {
target = [target targetForAction:action withSender:sender];
if (!target) {
return NO;
}
NSMethodSignature *signature = [target methodSignatureForSelector:action];
const NSInteger hiddenArgumentCount = 2; // self and _cmd
NSInteger argumentCount = [signature numberOfArguments] - hiddenArgumentCount;
switch (argumentCount) {
case 0:
SuppressPerformSelectorLeakWarning([target performSelector:action]);
break;
case 1:
SuppressPerformSelectorLeakWarning([target performSelector:action withObject:sender]);
break;
case 2:
SuppressPerformSelectorLeakWarning([target performSelector:action withObject:sender withObject:userInfo]);
break;
default:
NSAssert(NO, @"Invalid number of arguments.");
break;
}
return YES;
}
@end
注意:这使用SuppressPerformSelectorLeakWarning宏。
这在UITableView中很有用。想象一下,您在单元格上有一个手势识别器,您需要通知视图控制器已执行的操作。您可以使用响应程序链,而不必为单元格创建委托,然后将消息转发给委托。
样本用法(发件人):
// in table view cell class:
- (void)longPressGesture:(UILongPressGestureRecognizer *)recognizer {
// ...
[ABCResponderChainHelper sendResponderChainAction:@selector(longPressCell:) to:self from:self withUserInfo:nil];
}
这里self
指的是细胞本身。 responder chain确保首先将该方法发送到UITableViewCell
,然后发送到UITableView
,最后发送到UIViewController
。
样本用法(接收者):
#pragma mark - Custom Table View Cell Responder Chain Messages
- (void)longPressCell:(UITableViewCell *)sender {
// handle the long press
}
如果您尝试使用sendAction:to:from:forEvent:执行相同的操作,则会遇到两个问题:
nil
传递给to
参数,此时它将在第一响应者处开始发送消息而不是你选择的对象。控制第一响应者可能很麻烦。使用此方法,您只需告诉它要从哪个对象开始。UIEvent
并添加userInfo
等属性并在事件参数中传递它;在这种情况下,这是一个笨拙的解决方案。