iOS - forward all touches through a view

Disabling user interaction was all I needed!

Objective-C:

myWebView.userInteractionEnabled = NO;

Swift:

myWebView.isUserInteractionEnabled = false

For passing touches from an overlay view to the views underneath, implement the following method in the UIView:

Objective-C:

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    NSLog(@"Passing all touches to the next view (if any), in the view stack.");
    return NO;
}

Swift 5:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    print("Passing all touches to the next view (if any), in the view stack.")
    return false
}

This is an old thread, but it came up on a search, so I thought I'd add my 2c. I have a covering UIView with subviews, and only want to intercept the touches that hit one of the subviews, so I modified PixelCloudSt's answer to

-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    for (UIView* subview in self.subviews ) {
        if ( [subview hitTest:[self convertPoint:point toView:subview] withEvent:event] != nil ) {
            return YES;
        }
    }
    return NO;
}

Improved version of @fresidue answer. You can use this UIView subclass as transparent view passing touches outside its subview. Implementation in Objective-C:

@interface PassthroughView : UIView

@end

@implementation PassthroughView

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
    for (UIView *view in self.subviews) {
        if (!view.hidden && [view pointInside:[self convertPoint:point toView:view] withEvent:event]) {
            return YES;
        }
    }
    return NO;
}

@end

.

and in Swift:

class PassthroughView: UIView {
  override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
    return subviews.contains(where: {
      !$0.isHidden
      && $0.isUserInteractionEnabled
      && $0.point(inside: self.convert(point, to: $0), with: event)
    })
  }
}

TIP:

Say then you have a large "holder" panel, perhaps with a table view behind. You make the "holder" panel PassthroughView. It will now work, you can scroll the table "through" the "holder".

But!

  1. On top of the "holder" panel you have some labels or icons. Don't forget, of course those must simply be marked user interaction enabled OFF!

  2. On top of the "holder" panel you have some buttons. Don't forget, of course those must simply be marked user interaction enabled ON!

  3. Note that somewhat confusingly, the "holder" itself - the view you use PassthroughView on - must be marked user interaction enabled ON! That's ON!! (Otherwise, the code in PassthroughView simply will never be called.)


I needed to pass touches through a UIStackView. A UIView inside was transparent, but the UIStackView consumed all touches. This worked for me:

class PassThrouStackView: UIStackView {

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if view == self {
            return nil
        }
        return view
    }
}

All arrangedSubviews still receive touches, but touches on the UIStackView itself went through to the view below (for me a mapView).