走响应链以传递自定义事件.这是错的吗?

Phi*_*ert 18 iphone events cocoa ios

根据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];
}
Run Code Online (Sandbox Code Playgroud)

这很有效,但我觉得应该有其他方法来处理这个问题.手动走链子这种方式似乎不太好......

请注意,通知在这里不是一个好的解决方案,因为我只希望参与视图层次结构中的对象,并且通知是全局的.

在iOS中处理这个问题的最佳方法是什么(和Cocoa一样)?

编辑:

我想要完成什么?

我有一个视图控制器,它有一个视图,有子视图等...几个子视图是一个特定类型,显示数据库中的项目.当用户点击此视图时,应将信号发送到控制器以导航到此项目的详细信息页面.

处理点击的视图位于视图层次结构中主视图下方的几个级别.我必须告诉控制器(或在某些情况下,特定的子视图"向上链")选择了一个项目.

听通知是一种选择,但我不喜欢这种解决方案,因为选择一个项目不是全局事件.它严格依赖于当前的视图控制器.

Pet*_*sey 14

UIApplication有一个方法就是为了这个目的,它的Cocoa表兄也是如此.您可以使用一条消息替换问题中的所有代码.

  • 我已经读过这篇文章,不知怎的,我没有注意到文档中的一行:*“如果目标为零,应用程序将消息发送到第一个响应者,从那里它沿着响应者链向上前进,直到被处理。” *。非常感谢您的回答!很有帮助! (2认同)

Mik*_*lah 12

你很亲密.更标准的是这样的事情:

@implementation NSResponder (MyViewController)
- (void)itemSelected:(id)someItem
{
    [[self nextResponder] itemSelected:someItem];
}
@end
Run Code Online (Sandbox Code Playgroud)

这通常是事件默认情况下如何传递链.然后在右侧控制器中,覆盖该方法,而不是采取自定义操作.

这可能不是您想要实现的正确模式,但它是将消息传递到响应者链的好方法.


Sen*_*ful 5

如果您确定第一响应者设置正确,则 Peter 的解决方案有效。如果你想更多地控制哪个对象被通知事件,你应该使用targetForAction:withSender:代替。

这允许您指定应该能够处理事件的第一个视图,然后它会从那里爬上响应者链,直到找到可以处理消息的对象。

这是您可以使用的完整记录的功能:

@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
Run Code Online (Sandbox Code Playgroud)

执行:

@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
Run Code Online (Sandbox Code Playgroud)

注意:这使用SuppressPerformSelectorLeakWarning宏。

这在 UITableView 中效果很好。想象一下,您在单元格上有一个手势识别器,您需要将执行的操作通知给视图控制器。您不必为单元创建委托,然后将消息转发给委托,而是可以使用响应者链。

示例用法(发件人):

// in table view cell class:
- (void)longPressGesture:(UILongPressGestureRecognizer *)recognizer {
    // ...
    [ABCResponderChainHelper sendResponderChainAction:@selector(longPressCell:) to:self from:self withUserInfo:nil];
}
Run Code Online (Sandbox Code Playgroud)

这里self指的是细胞本身。所述应答器 可确保该方法首先被发送到的UITableViewCell,那么UITableView,最终的UIViewController

示例用法(接收器):

#pragma mark - Custom Table View Cell Responder Chain Messages
- (void)longPressCell:(UITableViewCell *)sender {
    // handle the long press
}
Run Code Online (Sandbox Code Playgroud)

如果你试图用sendAction:to:from:forEvent:做同样的事情,你有两个问题:

  • 为了它与响应链的工作,你必须传递nilto参数,此时它会开始在消息第一反应者,而不是你所选择的对象。控制第一响应者可能很麻烦。使用此方法,您只需告诉它从哪个对象开始。
  • 您不能轻松地使用任意数据传递第二个参数。您需要子类化UIEvent并添加一个属性,例如userInfo并在事件参数中传递它;在这种情况下,一个笨拙的解决方案。