透明的UIControl控件上的触摸被忽略 - 不会被添加的动作函数处理。

7
我有一个包含几个其他控件的自定义UIControl。在这些控件之间有一些空白区域,而我的UIControl的背景需要是透明的。
我需要捕捉发生在我的自定义UIControl上的所有触摸事件,即使它们发生在其他控件之间(透明区域上)。我不能使用手势识别器,因为我需要更多的控制权。相反,我想像这样注册触摸处理函数:
myControl.addTarget(self, action: "handleTouchDown:event:", forControlEvents: UIControlEvents.TouchDown)

使用这种方法,我可以接收到在myControl的非透明区域发生的触摸事件,但是那些在透明背景上发生的触摸事件却无法接收到。

我尝试在自定义控件中重写hitTest::point:withEvent方法以不检查alpha值。但是当触摸事件发生在控件的透明区域时,hitTest::point:withEvent甚至没有被调用。我将控件的layer替换为自定义的CALayer,并在其中也覆盖了hitTest,但没有任何结果(似乎根本没有调用层上的hitTest)。


更多细节(编辑)

为了提供完美的答案(并赢得悬赏),您需要做到以下几点:

  1. 创建一个简单的应用程序,添加一个UIControl(例如UIButton)。
  2. UIControl中删除所有内容(从UIButton中删除文本),并使其背景透明(设置为清除颜色或将alpha通道设置为0)。
  3. 使用addTarget::action:forControlEvents:方法在控件上注册UIControlEvents.TouchDown事件。在处理程序方法中将一些内容打印到控制台。
  4. 运行应用程序,按下控件。没有任何内容被打印到控制台。使其工作-不要使用手势识别器,我需要addTarget::action:forControlEvents:提供的粒度。不要使用hack解决方案。我知道将控件的背景alpha通道设置为0.01会使它突然工作,但这是我不想要的hack。请在此处描述您所做的操作。

你确定你的 UIControl 在视图层次结构中处于正确的位置吗?检查一下你的视图层次结构的顺序。 - Michael
@Nikita 如果有其他视图覆盖它,那么不会接收到对于不透明区域的触摸事件。但是我确实会对不透明区域接收到触摸事件。这并不是什么奇怪的行为 - 控件通常就是这样运作的。尝试创建一个简单的项目,放置一些 UIControl 并将其完全设置为透明,然后尝试使用 addTarget 注册一些触摸事件。它将不起作用。设计真是太愚蠢了... - Rasto
我尝试使用UIControl,但是我注意到了同样的问题。我还按照这个 SO帖子尝试了hitTest:Point:withEvent,但它也不能正常工作。也许您可以将背景颜色设置为白色,并设置非常低的alpha值?或者甚至尝试使用UIButton - Michael
@Nikita 那篇帖子与我的问题无关。我没有覆盖视图 - 想象一下层次结构中只有一个透明视图,我想检测它上面的触摸。 - Rasto
1
通过手势识别器委托方法 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch,您可以精确地指定手势将传递到哪个识别器。难道您不能仅指定控件的视图接收触摸吗?(如果触摸位于其中一个子控制器上,则拒绝触摸。) - Tim Quinn
显示剩余5条评论
3个回答

2

在您的EDIT部分之后:

https://github.com/soxjke/TransparentControl

1)如果我将背景颜色设置为+[UIColor clearColor],触摸效果很好。因此,您无需进行更多操作,可以继续使用透明颜色。(顶部按钮)
2)如果我将alpha设置为0,则无法处理触摸。OK(中间按钮)
3)要处理这些触摸,有一个简单的解决方案(底部按钮),即子类化UIButton(实际上,您可以使用层次结构中的任何内容,直到UIView)。覆盖hitTest:withEvent:方法。

- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return CGRectContainsPoint(self.bounds, point) ? self : nil;
}

利润
4) 如果您需要深入了解,请使用

touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
touchesCancelled:withEvent:

在您的UIResponder子类上执行操作,就像Rob Glassey在他的答案中所提出的那样。

P.S. 最后离题一下。我不知道您实际的任务是什么,但是告诉您不能使用手势识别器,因为您需要对所有触摸事件进行“更多控制”,这说明您不了解手势识别器的可能性。因此,根据我的经验,我可以说您正在发明自行车而不是提供好的解决方案。

P.P.S 如果我和其他人提出的方法对您无效-请检查您的控件的userInteractionEnabled

附录(视图控制器代码以测试:)

#import "ViewController.h"
#import "TransparentControl.h"

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UIButton *buttonClearColor;
@property (weak, nonatomic) IBOutlet UIButton *buttonAlpha0;
@property (weak, nonatomic) IBOutlet TransparentControl *customButtonAlpha0;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.buttonClearColor addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside];
    [self.buttonClearColor addTarget:self action:@selector(touchUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
    [self.buttonClearColor addTarget:self action:@selector(touchDown:) forControlEvents:UIControlEventTouchDown];

    [self.buttonAlpha0 addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside];
    [self.buttonAlpha0 addTarget:self action:@selector(touchUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
    [self.buttonAlpha0 addTarget:self action:@selector(touchDown:) forControlEvents:UIControlEventTouchDown];

    [self.customButtonAlpha0 addTarget:self action:@selector(touchUpInside:) forControlEvents:UIControlEventTouchUpInside];
    [self.customButtonAlpha0 addTarget:self action:@selector(touchUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
    [self.customButtonAlpha0 addTarget:self action:@selector(touchDown:) forControlEvents:UIControlEventTouchDown];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

- (void)touchUpInside:(id)sender
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

- (void)touchDown:(id)sender
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

- (void)touchUpOutside:(id)sender
{
    NSLog(@"%s", __PRETTY_FUNCTION__);
}

@end

1

hitTest的文档提到完全透明的东西会被忽略,因此覆盖hitTest本身可能不足以解决这个问题,因为调用hitTest的任何内容在对象透明时都不会调用它。

相反,如果您需要无论如何访问原始触摸事件,请尝试降级到较低级别的UIResponder触摸方法(这些方法由UIViewUIControl继承,因此可供您使用)。

它们是:

touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
touchesCancelled:withEvent:

第一个参数是一个NSSet类型的触摸集合,第二个参数是一个UIEvent类型,就像你在其他方法中提到的那样...
通过这种方式,您无需添加目标操作,而是覆盖自定义控件上的这些方法。这些方法较低级别(并且极端老式),但应该可以完全控制触摸事件。

谢谢您的回答。我也尝试过那个方法。然而,即使是透明的UIControlUIResponder触摸方法(例如touchesBegan:withEvent:等)也不会被调用。虽然这是一个很好的想法并且我期望它能够起作用,但不幸的是它并没有。 - Rasto
在这种情况下,我认为问题可能是触摸被上面的控件(您说“包含几个其他控件”的地方)阻止了?它们处理自己的触摸吗?如果只有一个背景控件(删除其他控件),它是否仍然失败?需要查看Petro在他的答案中提到的userInteractionEnabled - 即使那些视图是透明的,如果它们没有完全正确配置,任何类型的视图都很容易阻止触摸。 - Rob Glassey

1
我使用一个空的drawRect方法来继承UIControl,并且它有效。
根据文档,opaqueUIButton和其他一些控件忽略,因此不能用作此技术的控制点。但有趣的是,视图的默认背景颜色是透明的(nil)。
通过继承UIControl并设置opaque = NO,您可以创建一个drawRect方法,该方法不完全填充框架,并允许“透明”区域而不设置alpha = 0,从而允许hitTest:withEvent:仍然捕捉事件。由于元素是一个UIView,所以您应该能够添加视图,然后实现自己的drawRect,调用所有子视图的等效函数,同时不绘制应该是透明的区域。
我的基本ViewController元素,ImageView是为了确保它工作正常。
@implementation MyViewController
- (void)viewDidLoad {
    [ super viewDidLoad ];

    transparentControl = [ [ TransparentControl alloc ] initWithFrame:CGRectMake( 0, 0, 400, 400 ) ];
    [ transparentControl addTarget:self action:@selector(printText) forControlEvents:UIControlEventTouchUpInside];

    // Create an image view below the button for proof the control is transparent
    UIImageView * imageView = [ [ UIImageView alloc ] initWithImage:[ UIImage imageNamed:@"BGImage.jpg" ] ];
    imageView.frame = self.view.frame;

    [ self.view addSubview:imageView ];
    [ self.view addSubview:transparentControl ];
}

 -( void )printText {
    NSLog( @"Hello, this is a transparent button." );
}
@end

还有我的透明控件。

@implementation TransparentControl

- ( instancetype )initWithFrame:( CGRect )frame {
    if( self = [ super initWithFrame:frame ] ) {
        self.opaque = NO;
        self.userInteractionEnabled = YES;
    }
    return self;
}

- ( void )drawRect:(CGRect)rect {
}
@end

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