Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom UIControl and Gesture Recognizer

I am trying to create a custom UIControl similar to a slider.

This control is to be the subview of a view that also has a tap gesture recognizer attached to it.

The problem now is that this tap gesture recognizer cancels the touches sent to my control. Is there a way I can override this from within the code of my control?

If I look into the standard controls in iOS it looks as if UIButton has a way of overriding the tap gesture recognizer but UISlider doesn't. So if I replace my custom control with a UIButton the tap gesture recognizer does not trigger its action, but if I replace it with a slider it does.

edit: I made a small project in Xcode to play around in. Download here https://dl.dropboxusercontent.com/u/165243/TouchConcept.zip and try to change it so that

  • The UICustomControl does not know about the tap gesture recognizer
  • The UICustomControl is not cancelled when the user taps down on the yellow box
  • The UICustomControl does not inherit from UIButton (that is a solution that does not feel right and might give me more headaches later on)

The code:

// inherit from UIButton will give the wanted behavior, inherit from UIView (or UIControl) gives
// touchesCancelled by the gesture recognizer
@interface UICustomControl : UIView

@end

@implementation UICustomControl

-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{   NSLog(@"touchesBegan"); }

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{   NSLog(@"touchesMoved"); }

-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{   NSLog(@"touchesEnded"); }

-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{   NSLog(@"touchesCancelled"); }

@end
@interface ViewController ()

@end


@implementation ViewController

- (void)viewDidLoad
{
    UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(logTap:)];
    [self.view addGestureRecognizer:tapRecognizer];
    UIView *interceptingView = [[UICustomControl alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
    interceptingView.userInteractionEnabled = YES;
    interceptingView.backgroundColor = [UIColor yellowColor];
    [self.view addSubview: interceptingView];
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void) logTap: (id) sender
{
    NSLog(@"gesture recognizer fired");
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
like image 864
Kristof Van Landschoot Avatar asked Dec 15 '22 06:12

Kristof Van Landschoot


2 Answers

You can configure the gesture recognizer to not cancel touches in the view it's attached using the "cancels touches in view" property:

myGestureRecognizer.cancelsTouchesInView = NO;
like image 188
David Rönnqvist Avatar answered Jan 01 '23 17:01

David Rönnqvist


I'm a little bit late, but for those (like me) stumbling into this question, I used an alternative solution:

Use the delegate of the gesture recogniser.

UITapGestureRecognizer *tapGestRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissInfoBox:)];
tapGestRec.delegate = self;
[self.view addGestureRecognizer:tapGestRec];

Then do a sort of hit test in the shouldReceiveTouch delegate function when the gesture recogniser wants to handle/swallow a touch.

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    CGPoint location = [touch locationInView:self.view];
    return !CGRectContainsPoint(self.myCustomControl.frame, location) && !CGRectContainsPoint(self.myOtherCustomControl.frame, location);
}

I did all this in my ViewController, this way the UIControl does not have to know about any sibling views and the gesture recogniser does not 'steal' taps from my custom controls and only handles 'uncaught' taps.

Also, this way you won't trigger both the gesture recogniser and the custom control, which would happen with cancelsTouchesInView.

BTW, maybe it works with UIButton because UIButton uses gesture recognisers internally? I think they understand each other, while UIControls and recognisers do not. Not sure though.

like image 22
Jeroen Bouma Avatar answered Jan 01 '23 16:01

Jeroen Bouma