20

I have the following setup.

+- XXXCustomControl : UIControl -------+
| A                                    |
|   +- ContentView -------------------+|
|   |                                 ||
|   |  B                              ||
|   |                                 ||
|   +---------------------------------+|
+--------------------------------------+ 

A XXXCustomControl that is a subclass of UIControl. It contains one subview called contentView of type UIView with size that is smaller than the Control's area.. That view has .userInteractionEnabled = YES;

I need that property to have set to YES, because horizontal scrollviews are put inside this once in a while and they need to be scrollable. If the superview (in our case content view would not allow user interaction, this is inherited y the subviews.) But at the same time this XXXCustomControl need to be tappable when it contains no scrollview in its content view not only in area A but also in area B.

So I have a "conflict of interests" here because I either

1) set the content view to userInteractionEnabled = NO, then I can tap the empty control in the content view area both in A and B, but the scrollviews I will put there won't be scrollable..

2) set the content view to userInteractionEnabled = YES but then, if the Control s empty, I can only tap area A to trigger a touch event.

One idea I came up with is that I set the property to NO by default and when I populate the contentView I set it to yes. when I clear the contentView I set the property back to no. Basically I want this to have set to yes all the time, and when it is empty ,force the contentView to pass the touchUpInside event up to its superview.

Is this possible?

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
Earl Grey
  • 7,426
  • 6
  • 39
  • 59
  • 1
    If you only care about tap why not add a gesturerecognizer to the child view that's action is the same as the gesturerecognizer in the parent view? That seems like the easiest implementation. – Putz1103 May 06 '14 at 14:36
  • Why didn't you award an answer? michaels works for me... – David H Jul 25 '16 at 23:58
  • Better late than never... – Earl Grey Jul 29 '16 at 12:40
  • 1
    I think that's not the definition of a conflict of interest, maybe conflicted interests? :) –  Jul 07 '17 at 11:52

6 Answers6

21

You could try overriding the pointInside:withEvent: method in your inner view. This will allow you to return NO when you want to forward touches to the superview:

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    if( /* You have content, and you want to receive touches */){
        return YES;
    }else{
        return NO;
    }
}
mspensieri
  • 3,501
  • 15
  • 18
  • This also works well for a view contained in a container view - to allow some controls to exist in the container (super) view. Just interate through the subview frames - if no hit then return NO – David H Jul 25 '16 at 23:59
4

You can subclass your subview, and implement

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    if (!touchedContent) {
        [[self nextResponder] touchesBegan:touches withEvent:event];
    } else {
        [super touchesBegan:touches withEvent:event];
    }
}
Leandros
  • 16,805
  • 9
  • 69
  • 108
1

You can use another UIView to "hide" the subview.

*Details : This UIView is the subview's sibling . it has the same size and position of your subview with background color of clear color.

*Explain: The new UIView will take the touch, and automatically pass it to the superview.

This way doesn't need to subclass your subview - it's a storyboard solution.
Please try this. Have fun!

letanthang
  • 390
  • 5
  • 7
0

Use this in super view, without changing subviews code (since subviews may be introduced from other frameworks or pods):

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    UIView *view = [super hitTest:point withEvent:event];
    if ([view isDescendantOfView:self])
    {
        NSLog(@"touched inside");
    }
    return view;
}
OpenThread
  • 2,096
  • 3
  • 28
  • 49
0

- Swift

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    if <#condition#> { return true }
    else { return false }
}
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
0

If you want to handle the event from superview(XXXCustomControl in your case) instead of inner view(ContentView in your case), you can write the following swift code in your superview:

override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
    guard let view = super.hitTest(point, with: event) else { return nil }
    if view.isDescendant(of: self) {
        // your custom logic
    }
    return view
}
sabius
  • 704
  • 7
  • 10