i have a transparent UIScrollView on top of another view.
the scroll view has content - text and images, used to display info.
the view behind it has some images
Here I present my complete solution that:
Here the interface:
/**
* This subclass of UIScrollView allow views in a deeper Z index to react when touched, even if the scrollview instance is in front of them.
**/
@interface MJForwardingTouchesScrollView : UIScrollView
/**
* Set of Class objects. The scrollview will events pass through if the initial tap is not over a view of the specified classes.
**/
@property (nonatomic, strong) NSSet *forwardsTouchesToClasses;
/**
* Optional array of underlying views to test touches forward. Default is nil.
* @discussion By default the scroll view will attempt to forward to views located in the same self.superview.subviews array. However, optionally by providing specific views inside this property, the scroll view subclass will check als among them.
**/
@property (nonatomic, strong) NSArray <__kindof UIView*> *underlyingViews;
@end
And the Implementation:
#import "MJForwardingTouchesScrollView.h"
#import "UIView+Additions.h"
@implementation MJForwardingTouchesScrollView
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self != nil)
{
_forwardsTouchesToClasses = [NSSet setWithArray:@[UIControl.class]];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
_forwardsTouchesToClasses = [NSSet setWithArray:@[UIControl.class]];
}
return self;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL pointInside = [self mjz_mustCapturePoint:point withEvent:event];
if (!pointInside)
return NO;
return [super pointInside:point withEvent:event];
}
#pragma mark Private Methods
- (BOOL)mjz_mustCapturePoint:(CGPoint)point withEvent:(UIEvent*)event
{
if (![self mjz_mustCapturePoint:point withEvent:event view:self.superview])
return NO;
__block BOOL mustCapturePoint = YES;
[_underlyingViews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (![self mjz_mustCapturePoint:point withEvent:event view:obj])
{
mustCapturePoint = NO;
*stop = YES;
}
}];
return mustCapturePoint;
}
- (BOOL)mjz_mustCapturePoint:(CGPoint)point withEvent:(UIEvent *)event view:(UIView*)view
{
CGPoint tapPoint = [self convertPoint:point toView:view];
__block BOOL mustCapturePoint = YES;
[view add_enumerateSubviewsPassingTest:^BOOL(UIView * _Nonnull testView) {
BOOL forwardTouches = [self mjz_forwardTouchesToClass:testView.class];
return forwardTouches;
} objects:^(UIView * _Nonnull testView, BOOL * _Nullable stop) {
CGRect imageFrameInSuperview = [testView.superview convertRect:testView.frame toView:view];
if (CGRectContainsPoint(imageFrameInSuperview, tapPoint))
{
mustCapturePoint = NO;
*stop = YES;
}
}];
return mustCapturePoint;
}
- (BOOL)mjz_forwardTouchesToClass:(Class)class
{
while ([class isSubclassOfClass:NSObject.class])
{
if ([_forwardsTouchesToClasses containsObject:class])
return YES;
class = [class superclass];
}
return NO;
}
@end
The only extra code used is inside the UIView+Additions.h
category, which contains the following method:
- (void)add_enumerateSubviewsPassingTest:(BOOL (^_Nonnull)(UIView * _Nonnull view))testBlock
objects:(void (^)(id _Nonnull obj, BOOL * _Nullable stop))block
{
if (!block)
return;
NSMutableArray *array = [NSMutableArray array];
[array addObject:self];
while (array.count > 0)
{
UIView *view = [array firstObject];
[array removeObjectAtIndex:0];
if (view != self && testBlock(view))
{
BOOL stop = NO;
block(view, &stop);
if (stop)
return;
}
[array addObjectsFromArray:view.subviews];
}
}
Thanks