【问题标题】:Touches on transparent UIControl are ignored - not handled by added action functions透明 UIControl 上的触摸被忽略 - 不由添加的动作函数处理
【发布时间】:2014-12-26 00:16:30
【问题描述】:

我有一个自定义的UIControl,其中包含一些其他控件。在这些控件之间有空白空间,我的UIControl 的背景需要是透明的。

我需要捕捉发生在我的自定义 UIControl 上的所有触摸事件,即使它们仅发生在其他控件之间(在透明区域上)。我不能使用手势识别器,我需要比他们提供的更多控制。相反,我想注册触摸处理功能,如下所示:

myControl.addTarget(self, action: "handleTouchDown:event:", forControlEvents: UIControlEvents.TouchDown)

通过这种方法,我收到了在myControl 的非透明区域上发生的触摸,但不是那些在透明背景下发生的触摸。

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


更多细节(编辑)

要提供完美的答案(并赢得赏金),您需要做的就是:

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

【问题讨论】:

  • 如果我将 UIControl 的背景颜色设置为带有 alpha 0.01 的颜色,一切正常,我会得到我需要的所有触摸事件。但这就是黑客行为,它正在影响透明控件的视图颜色。
  • 你试过view.backgroundColor = [UIColor clearColor]吗?
  • @Nikita 是的,我确实做到了。它没有帮助。
  • 您确定您的UIControl 在视图层次结构中的正确位置吗?检查视图层次结构的顺序。
  • 使用手势识别器委托方法- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch,您可以精确地指定手势将传递给识别器的位置。您不能只指定控件的视图来接收触摸吗? (如果触摸超过您的一个子控制器,则拒绝触摸。)

标签: ios cocoa-touch uiview hittest uicontrol


【解决方案1】:

按照您的 EDIT 部分:

https://github.com/soxjke/TransparentControl

1) 如果我将背景颜色设置为+[UIColor clearColor],则效果非常好。所以你没有必要做更多的事情,用清晰的颜色去吧。 (顶部按钮)
2) 如果我设置 alpha = 0,则不处理触摸。确定(中键)
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 子类上,例如在他的answer 中提出的Rob Glassey

附:我将以题外话结束。不知道您的任务实际上是什么,但是告诉您不能使用识别器,因为您需要对所有触摸事件“更多控制”会发现您不知道手势识别器的可能性。所以,从我的经验来看,我会说你宁愿发明自行车而不是做好的解决方案。

附言如果我和其他人在这里提出的方法对您不起作用 - 请检查您的控件的 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

【讨论】:

    【解决方案2】:

    我用一个空的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
    

    【讨论】:

      【解决方案3】:

      hitTest 的文档提到完全透明的东西被忽略了,因此覆盖 hitTest 本身可能不足以解决这个问题,因为当对象透明时,无论调用 hitTest 都不会调用它.

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

      他们是:

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

      第一个参数是触摸的NSSet,第二个参数是UIEvent,就像您在其他方法中所指的那样......

      有了这个,您不需要添加目标操作,而是在您的自定义控件上覆盖这些方法。这些级别较低(极端老式),但应该让您完全控制触摸事件。

      【讨论】:

      • 感谢您的回答。我也尝试过。 UIResponder 触摸方法(touchesBegan:withEvent: 等)在透明的 UIControls 上也不会被调用(!)。虽然下降到UIResponder 这是一个好主意,但不幸的是,我希望它不会工作。
      • 在这种情况下,我认为问题可能是触摸被上面的控件阻止(您说“包含其他一些控件”)?他们会处理自己的接触吗?如果您只有一个后台控件(删除其他控件),它仍然会失败吗? Petro 在他关于 userInteractionEnabled 的回答中提到的内容需要查看 - 上面任何类型的视图都很容易阻止触摸,即使这些视图是透明的,如果它们配置不完美的话。跨度>
      猜你喜欢
      • 1970-01-01
      • 2011-03-21
      • 1970-01-01
      • 2012-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-16
      • 2012-12-05
      相关资源
      最近更新 更多