如何使superview拦截按钮触摸事件?

时间:2009-08-15 05:08:39

标签: iphone objective-c cocoa-touch

说我有这段代码:

#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 个答案:

答案 0 :(得分: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];

答案 1 :(得分:4)

Instinct会说“子类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对象本身。

答案 2 :(得分:3)

我建议使用这种方法:

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

    [self.view addGestureRecognizer:tapRecognizer];

您可以从superview执行此操作,无需创建自定义UI元素。

答案 3 :(得分:1)

感谢您的建议和内容丰富的回复。我最后只使用this页面上显示的说明:(在“Trick 1:使用单个UIScrollView模拟照片应用程序滑动/缩放/滚动”下)。

答案 4 :(得分:0)

我相信你必须在这种情况下继承UIButton并覆盖对UIResponder的触摸和动作事件的响应。然后,您可以将它们转发到您想要假定UIButton子类可以到达的任何对象。

在这种情况下,您可以将它们传递给UIButton的超级视图。

@interface MyButton : UIButton
@end

@implementation MyButton

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

@end

请参阅UIResponder documentation