2017-01-27 21:26:31 +01:00
|
|
|
//
|
2018-12-17 20:07:35 +01:00
|
|
|
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
2017-01-27 21:26:31 +01:00
|
|
|
//
|
|
|
|
|
|
|
|
#import "OWSAnyTouchGestureRecognizer.h"
|
|
|
|
#import <UIKit/UIGestureRecognizerSubclass.h>
|
|
|
|
|
2018-12-17 20:07:35 +01:00
|
|
|
NS_ASSUME_NONNULL_BEGIN
|
|
|
|
|
|
|
|
NSString *NSStringForUIGestureRecognizerState(UIGestureRecognizerState state)
|
|
|
|
{
|
|
|
|
switch (state) {
|
|
|
|
case UIGestureRecognizerStatePossible:
|
|
|
|
return @"UIGestureRecognizerStatePossible";
|
|
|
|
case UIGestureRecognizerStateBegan:
|
|
|
|
return @"UIGestureRecognizerStateBegan";
|
|
|
|
case UIGestureRecognizerStateChanged:
|
|
|
|
return @"UIGestureRecognizerStateChanged";
|
|
|
|
case UIGestureRecognizerStateEnded:
|
|
|
|
return @"UIGestureRecognizerStateEnded";
|
|
|
|
case UIGestureRecognizerStateCancelled:
|
|
|
|
return @"UIGestureRecognizerStateCancelled";
|
|
|
|
case UIGestureRecognizerStateFailed:
|
|
|
|
return @"UIGestureRecognizerStateFailed";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-27 21:26:31 +01:00
|
|
|
@implementation OWSAnyTouchGestureRecognizer
|
|
|
|
|
|
|
|
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)canBePreventedByGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
|
|
|
|
{
|
|
|
|
[super touchesBegan:touches withEvent:event];
|
|
|
|
|
|
|
|
if (self.state == UIGestureRecognizerStatePossible && [self isValidTouch:touches event:event]) {
|
|
|
|
self.state = UIGestureRecognizerStateRecognized;
|
|
|
|
} else {
|
|
|
|
self.state = UIGestureRecognizerStateFailed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-21 21:37:51 +02:00
|
|
|
- (UIView *)rootViewInViewHierarchy:(UIView *)view
|
|
|
|
{
|
2018-09-06 19:01:24 +02:00
|
|
|
OWSAssertDebug(view);
|
2017-04-21 21:37:51 +02:00
|
|
|
UIResponder *responder = view;
|
|
|
|
UIView *lastView = nil;
|
|
|
|
while (responder) {
|
|
|
|
if ([responder isKindOfClass:[UIView class]]) {
|
|
|
|
lastView = (UIView *)responder;
|
|
|
|
}
|
|
|
|
responder = [responder nextResponder];
|
|
|
|
}
|
|
|
|
return lastView;
|
|
|
|
}
|
|
|
|
|
2017-01-27 21:26:31 +01:00
|
|
|
- (BOOL)isValidTouch:(NSSet<UITouch *> *)touches event:(UIEvent *)event
|
|
|
|
{
|
|
|
|
if (event.allTouches.count > 1) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
if (touches.count != 1) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
UITouch *touch = touches.anyObject;
|
|
|
|
CGPoint location = [touch locationInView:self.view];
|
|
|
|
if (!CGRectContainsPoint(self.view.bounds, location)) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([self subviewControlOfView:self.view containsTouch:touch]) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2017-02-03 18:28:11 +01:00
|
|
|
// Ignore touches that start near the top or bottom edge of the screen;
|
|
|
|
// they may be a system edge swipe gesture.
|
2017-04-21 21:37:51 +02:00
|
|
|
UIView *rootView = [self rootViewInViewHierarchy:self.view];
|
|
|
|
CGPoint rootLocation = [touch locationInView:rootView];
|
|
|
|
CGFloat distanceToTopEdge = MAX(0, rootLocation.y);
|
|
|
|
CGFloat distanceToBottomEdge = MAX(0, rootView.bounds.size.height - rootLocation.y);
|
2017-02-03 18:28:11 +01:00
|
|
|
CGFloat distanceToNearestEdge = MIN(distanceToTopEdge, distanceToBottomEdge);
|
|
|
|
CGFloat kSystemEdgeSwipeTolerance = 50.f;
|
|
|
|
if (distanceToNearestEdge < kSystemEdgeSwipeTolerance) {
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2017-01-27 21:26:31 +01:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)subviewControlOfView:(UIView *)superview containsTouch:(UITouch *)touch
|
|
|
|
{
|
|
|
|
for (UIView *subview in superview.subviews) {
|
|
|
|
if (subview.hidden || !subview.userInteractionEnabled) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
CGPoint location = [touch locationInView:subview];
|
|
|
|
if (!CGRectContainsPoint(subview.bounds, location)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ([subview isKindOfClass:[UIControl class]]) {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
if ([self subviewControlOfView:subview containsTouch:touch]) {
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
2018-12-17 20:07:35 +01:00
|
|
|
|
|
|
|
NS_ASSUME_NONNULL_END
|