繁体   English   中英

将事件传递给响应者链中的下一个响应者的技巧是什么?

[英]What's the trick to pass an event to the next responder in the responder chain?

苹果真的很有趣。 我的意思是,他们说这有效:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch* touch = [touches anyObject];
    NSUInteger numTaps = [touch tapCount];
    if (numTaps < 2) {
        [self.nextResponder touchesBegan:touches withEvent:event];
   } else {
        [self handleDoubleTap:touch];
   }
}

我有一个视图控制器。 如您所知,视图控制器继承自 UIResponder。 该视图控制器创建一个继承自 UIView 的 MyView 对象,并将其作为子视图添加到它自己的视图中。

所以我们有:

视图控制器 > 有一个视图(自动) > 有一个 MyView(这是一个 UIView)。

现在在 MyView 里面,我把上面的代码和一个打印“touched MyView”的 NSLog 放在一起。 但是我将事件转发给下一个响应者,就像上面一样。 在 ViewController 中,我有另一个 touchesBegan 方法,它只是打印一个 NSLog 一个“触摸视图控制器”。

现在猜猜看:当我触摸 MyView 时,它会打印“touched MyView”。 当我触摸 MyView 外部时,即 VC 的视图,我得到一个“触摸视图控制器”。 所以两者都有效! 但不起作用的是转发事件。 因为现在,实际上下一个响应者应该是视图控制器,因为中间没有其他东西。 但是VC的事件处理方法在我转发的时候从来没有被调用过。

%$&!§!!

想法?

找出奇怪的东西MyView 的下一个响应者是视图控制器的视图。 这是有道理的,因为 MyView 是它的子视图。 但是我没有从视图控制器修改这个 UIView。 这没什么好习惯的。 它没有实现任何触摸事件处理。 消息不应该传递给视图控制器吗? 我怎么可能让它过去? 如果我删除 MyView 中的事件处理代码,那么事件就会很好地到达视图控制器。

根据一个类似的问题,你的方法应该有效。

这让我认为您的视图的nextResponder实际上并不是您怀疑的ViewController

我会在您的转发代码中添加一个快速NSLog来检查您的nextResponder真正指向的内容:

if (numTaps < 2) {
    NSLog(@"nextResponder = %@", self.nextResponder);
    [self.nextResponder touchesBegan:touches withEvent:event];
}

您还可以更改其他 NSLog 消息,以便它们输出类型和地址信息:

NSLog(@"touched %@", self);

touched <UIView 0x12345678>

这应该让您开始诊断问题。

遇到了这个问题,因为我的自定义视图在视图层次结构中更深。 相反,我爬上了响应者链,直到它找到一个UIViewController

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    // Pass to top of chain
    UIResponder *responder = self;
    while (responder.nextResponder != nil){
        responder = responder.nextResponder;
        if ([responder isKindOfClass:[UIViewController class]]) {
            // Got ViewController
            break;
        }
    }
    [responder touchesBegan:touches withEvent:event];
}

如果您查看UIResponder的文档,说明默认情况下是这样的:

UIView 通过返回管理它的 UIViewController 对象(如果有)或它的超视图(如果没有)来实现这个方法;

如果您的视图没有返回视图控制器,那么它必须返回其超级视图(如您所见)。

UIApplication 实际上有一个方法可以准确地处理这个用例:

- (BOOL)sendAction:(SEL)action
                to:(id)target
              from:(id)sender
          forEvent:(UIEvent *)event

由于您没有对目标的引用,您可以通过将目标值设置为nil来在响应者链中向上工作,因为根据文档:

如果 target 为 nil,应用程序将消息发送给第一响应者,从那里它沿着响应者链向上推进,直到它被处理。

您当然可以使用其类方法[UIApplication sharedApplication]访问[UIApplication sharedApplication]

更多信息: UIApplication 文档

这是额外的,但如果你绝对需要访问视图控制器来做一些更复杂的事情,你可以手动沿着响应者链向上工作,直到通过调用nextResponder方法到达视图控制器,直到它返回一个视图控制器。 例如:

UIResponder *responder = self;
while ([responder isKindOfClass:[UIView class]])
    responder = [responder nextResponder];

然后你可以把它作为你的视图控制器,然后继续做你想做的任何事情:

UIViewController *parentVC = (UIViewController *)responder

但请注意,这种破坏了 MVC 模式,并且可能意味着您正在以“hacky”方式做某事

要引用视图控制器,您需要使用

[[self nextResponder] nextResponder]

因为[self nextResponder]表示视图控制器的视图,而[[self nextResponder] nextResponder]表示视图控制器本身,现在您可以获取触摸事件

是的,它应该在普通情况下 100% 工作。 我刚刚尝试过测试项目,一切似乎都没问题。

我已经创建了基于视图的应用程序。 使用 Interface Builder 将新的 UIView 放到当前的 View 中。 然后使用 UIView 的子类创建新文件,并为我的新视图选择刚刚创建的类。 (Interface Builder->Class identity->Class->MyViewClass)

为 MyViewClass 和 UIViewController 添加触摸处理函数。

//我的视图类

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(@"myview touches");
[self.nextResponder touchesBegan:touches withEvent:event];
}

//视图控制器

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {  
  NSLog(@"controller touches");
}   

按 MyViewClass 时,我会看到两个 NSLog。 在加载 ViewController 或使用 loadView 函数以编程方式设置视图时,您是否使用了 Interface Builder 和 XIB 文件?

今天我找到了一个更好的方法来解决这个问题或这样的问题。

在你的 UITableViewCell 中,你可以添加一个委托来监听触摸事件,如下所示:

@protocol imageViewTouchDelegate

-(void)selectedFacialView:(NSInteger)row item:(NSInteger)rowIndex;

@end

@property(nonatomic,assign)id<imageViewTouchDelegate>delegate;

然后在你的 UITableViewController: 你可以这样做:

@interface pressionTable : UITableViewController<facialViewDelegate>

在 .m 文件中,您可以实现此委托接口,例如:

-(void)selectedFacialView:(NSInteger)row item:(NSInteger)rowIndex{
//TO DO WHAT YOU WANT}

注意:当你初始化单元格时,你必须设置委托,否则委托无效,你可以这样做

- (UITableViewCell *)tableView:(UITableView *)tableView 
     cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

pressionCell *cell = (pressionCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    cell = [[pressionCell alloc]initWithFrame:CGRectMake(0, 0, 240, 40)];
}

cell.delegate = self;

}

PS:我知道这个DOC是旧的,但我仍然添加我的方法来解决这个问题,所以当人们遇到同样的问题时,他们会从我的答案中得到帮助。

我知道这篇文章很旧,但我想分享一下,因为我有类似的经历。 对于自动创建的视图控制器的视图,我通过将 userInteractionEnabled 设置为 NO 来修复它。

用这个

    [super.nextResponder touchesBegan:touches withEvent:event];

在您的代码中的这一行之前

    [self.nextResponder touchesBegan:touches withEvent:event];

它的意思是

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
     UITouch* touch = [touches anyObject];
     NSUInteger numTaps = [touch tapCount];
     if (numTaps < 2) {
         [super.nextResponder touchesBegan:touches withEvent:event];
         [self.nextResponder touchesBegan:touches withEvent:event];
     } else {
         [self handleDoubleTap:touch];
     }
}

**Swift 4 版本 **

func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    let touch = touches?.first as? UITouch
    let numTaps: Int? = touch?.tapCount
    if (numTaps ?? 0) < 2 {
        if let aTouches = touches as? Set<UITouch> {
            super.next?.touchesBegan(aTouches, with: event)
            next?.touchesBegan(aTouches, with: event)
        }
    } else {
        handleDoubleTap(touch)
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM