Touching transparent UIControl is ignored - not handled by added action functions

I have a custom UIControl that contains several other controls. There is an empty space between these controls, and the background of my UIControl should be transparent.

I need to catch all touch events that occur in my custom UIControl , even if they only occur between other controls (above transparent areas). I cannot use gesture recognizers, I need more control than they provide. Instead, I would like to register touch processing functions as follows:

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

With this aproach, I get strokes that happened with the opaque areas of myControl , but not those that happen with a transparent background.

I tried to override hitTest::point:withEvent in my custom control so as not to check the alpha value. But hitTest::point:withEvent is not even called when a touch occurs over a transparent control area. I replaced my layer control with a custom CALayer and redefined hitTest on this, too, with no result ( hitTest on the layer does not seem to be called at all).


Read more (EDIT)

To provide the perfect answer (and win generosity), all you have to do is:

  • Create a simple application, add one UIControl (for example, UIButton ).
  • Remove all content from UIControl (text from UIButton ) and make its background transparent (either set to clear the color, or set the alpha channel to 0).
  • Use the addTarget::action:forControlEvents: to register UIControlEvents.TouchDown events in the control. In the handler method, type something console.
  • Launch the application, press the control button. Nothing is printed on the console. Make it work - don't use gesture recognizers. I need the granularity provided by addTarget::action:forControlEvents: It is not recommended to use hacker solutions. I know that setting the background alpha channel on the 0.01 control will make it work all of a sudden, but it's a kind of hack that I don't want. Describe here what you have done.
+5
source share
3 answers

The documentation for hitTest mentions completely transparent things that are ignored, so it's possible that overriding hitTest not enough to get around this, since any calls to hitTest do not call it when the object is transparent.

Instead, I suggest you try falling for lower UIResponder touch methods if you need to access raw touch events no matter what (they are inherited by UIView and UIControl , therefore available to you).

It:

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

The first parameter is the NSSet touches, the second is a UIEvent , like what you talked about in your other methods ...

With this, you donโ€™t need to add a target action, but they override these methods in your custom control. This is a lower level (and in the old school as a last resort), but should give you full control over touch events.

+1
source

I subclassed UIControl using an empty drawRect method and it worked.

According to docs, opaque ignored by UIButton and some other controls, so it cannot be used as a reference point for this technique. Curiously, however, the default background color for the view is transparent (zero).

By subclassing UIControl and setting opaque = NO you can create a drawRect method that does not completely fill the frame and allows transparent areas without setting alpha = 0 , allowing hitTest:withEvent: Since the element is a UIView , you should be able to add views and then implement your own drawRect , which calls all the equivalent subviews functions without drawing areas that should be transparent.

My basic elements of the ViewController, ImageView , were supposed to make it work.

 @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 

And my transparent control.

 @implementation TransparentControl - ( instancetype )initWithFrame:( CGRect )frame { if( self = [ super initWithFrame:frame ] ) { self.opaque = NO; self.userInteractionEnabled = YES; } return self; } - ( void )drawRect:(CGRect)rect { } @end 
+1
source

After the EDIT section:

https://github.com/soxjke/TransparentControl

1) If I set the background color +[UIColor clearColor] , then the touch will work just fine. Therefore, you do not need to do anything else, go ahead with a clear color. (top button)
2) If I set alpha = 0, touches are not processed. OK (middle button)
3) To handle this touch, there is a simple solution (bottom button), a subclass of UIButton (in fact, you can go with anything in the hierarchy to UIView ). Override hitTest:withEvent:

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

PROFIT
4) If you need to go deeper, use

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

in a subclass of UIResponder , such as Rob Glassey , proposed in it.

PS I would finish the topic. I donโ€™t know what your task really is, but saying that you cannot use recognizers because you need to โ€œhave more controlโ€ on all touch events, discovering that you do not know the capabilities of gesture recognizers. So, judging from my experience, I would say that you rather invent a bicycle, and then make a good decision.

PPS If other guys suggested me, the methods will not work here - check your control userInteractionEnabled

Application (view controller code to check :):

 #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
source

Source: https://habr.com/ru/post/1205827/


All Articles