如何使父视图拦截按钮触摸事件?

6

假设我有以下代码:

#import <UIKit/UIKit.h>

@interface MyView : UIView
@end
@implementation MyView

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    // How can I get this to show up even when the button is touched?
    NSLog(@"%@", [touches anyObject]);
}

@end

@interface TestViewAppDelegate : NSObject <UIApplicationDelegate>
{
    UIWindow *window;
}

@end

@implementation TestViewAppDelegate

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    MyView *view = [[MyView alloc] initWithFrame:[window frame]];
    [view setBackgroundColor:[UIColor whiteColor]];

    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setTitle:@"Hiya!" forState:UIControlStateNormal];
    [button setFrame:CGRectMake(100.0, 100.0, 200.0, 200.0)];
    [view addSubview:button];

    [window addSubview:view];
    [window makeKeyAndVisible];
}


- (void)dealloc
{
    [window release];
    [super dealloc];
}

@end

有没有办法拦截发送到按钮的触摸事件?我希望最终能够创建一个UIView子类,当它检测到滑动时,通知它的视图控制器(或代理),以便可以将下一个视图控制器“推入”堆栈(类似于iPhone主屏幕)。我想这是第一步,但如果我方法不正确,我也愿意听取建议。

5个回答

16

我希望能看到其他的解决方案,但我知道最简单的方法是覆盖这两个UIView方法中的任意一个:

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event;

这些方法被调用以确定触摸是否在视图或其任何子视图的范围内,因此这是拦截触摸并将其传递的好时机。可以随意进行操作,然后

return [super hitTest:point withEvent:event];
或者
return [super pointInside:point withEvent:event];

难道你的意思不是返回 [superview hitTest:point withEvent:event]; 吗? - Or Arbel
“super” 是正确的,因为您需要调用类的父类方法,“superview” 会跳过这个步骤。 - keegan3d

4

本能地我们可能会想要“子类化UIButton”,但实际上,UIButton是一个类簇(即,实际对象类型根据您想要创建的按钮类型而透明地更改),这使得它极难被子类化。除了重写UIButton外,这种方法几乎无法做到什么。

当我需要解决一个几乎相同的问题时,我想出了一个使用组合来显示代理UIView的视图,但仍然允许拦截触摸的方法。它是这样工作的:

@interface MyView : UIView {
  UIView * visibleView;
}

- (id) initWithFrame:(CGRect)frame controlClass:(Class)class;

@end


@implementation MyView

- (id) initWithFrame:(CGRect)frame controlClass:(Class)class {
  if (self = [super initWithFrame:frame]) {
    CGRect visibleViewFrame = frame;
    visibleViewFrame.origin = CGPointZero;
    visibleView = [[class alloc] initWithFrame:visibleViewFrame];
    [visibleView setUserInteractionEnabled:NO];
    [self addSubview:visibleView];

    [self setUserInteractionEnabled:YES];
    [self setBackgroundColor:[UIColor clearColor]];
  }
  return self;
}

- (void) dealloc {
  [visibleView removeFromSuperview];
  [visibleView release], visibleView = nil;
  [super dealloc];
}

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  if (someCondition == YES) {
    [visibleView touchesBegan:touches withEvent:event];
  }
}

//and similar methods for touchesMoved:, touchesEnded:, etc

@end

基本思路是将按钮(或其他内容)嵌入到容器类中(类似于您在问题中描述的内容)。但是,您会禁用按钮的交互功能,这意味着当用户轻触按钮时,轻触事件将“穿透”按钮并进入包含父视图(在此示例中为MyView实例)中。从那里,您可以适当地处理触摸。如果要使按钮“响应”触摸,请通过发送适当的UIResponder消息来允许按钮这样做。(您可能需要首先重新启用visibleView上的userInteractionEnabled,我不确定。)
正如我上面所述,当我需要在界面上有一个按钮,但也要捕获UITouch对象本身时,我已经成功地使用了这种方法(不是这个确切的实现,而是这种模式)。

3
我建议使用以下方法代替:

您觉得这个方法如何:

    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
                                                 initWithTarget:self 
                                                 action:@selector(tapDetected:)];
    tapRecognizer.numberOfTapsRequired = 1;
    tapRecognizer.numberOfTouchesRequired = 1;

    [self.view addGestureRecognizer:tapRecognizer];

您可以从父视图中进行此操作,无需创建自定义UI元素。


很好,UITapGestureRecognizer在包含多个UILabel的UIView上运行得非常好。 - joelsand

1

感谢您的建议和信息回复。最终我只是使用了this页面上显示的解释:(在“技巧1:使用单个UIScrollView模拟照片应用程序的滑动/缩放/滚动”下)。


0

我相信在这种情况下,您需要对UIButton进行子类化,并覆盖UIResponder的触摸和动作事件响应。然后,您可以将它们转发到任何您想要的对象,假设UIButton子类可以访问它。

在这种情况下,您将把它们传递给UIButton的superview。

@interface MyButton : UIButton
@end

@implementation MyButton

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
     [[self superview] touchesMoved:touches withEvent:event];
}

@end

请查看UIResponder文档


1
UIButton是一个类族,不幸的是,这意味着它几乎无法被子类化。 - Dave DeLong
我不知道情况那么糟糕。他只是提到了一些UIView子类,所以我只是跟着他的示例代码使用了UIButton。 - criscokid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接