summaryrefslogtreecommitdiff
path: root/platform/iphone
diff options
context:
space:
mode:
authorSergey Minakov <naithar@icloud.com>2020-07-02 18:30:07 +0300
committerSergey Minakov <naithar@icloud.com>2020-07-26 16:05:58 +0300
commite6d7e01a72fed001a7adcae59648476a187985c0 (patch)
treea3e75bd738372b40bd2e27c8323da49e7ebfb5e2 /platform/iphone
parent5700429e4edca4ba90a406976ac3e8b337a50eb8 (diff)
iOS: added delay gesture recognizer
This gesture recognizer will prevent GodotView from processing unwanted gestures. Emulates UIScrollView behavior. Fires delayed touches on significant movement.
Diffstat (limited to 'platform/iphone')
-rw-r--r--platform/iphone/SCsub1
-rw-r--r--platform/iphone/godot_view.mm25
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.h44
-rw-r--r--platform/iphone/godot_view_gesture_recognizer.m171
4 files changed, 229 insertions, 12 deletions
diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub
index db64a14635..49c77468ed 100644
--- a/platform/iphone/SCsub
+++ b/platform/iphone/SCsub
@@ -18,6 +18,7 @@ iphone_lib = [
"godot_view.mm",
"display_layer.mm",
"godot_view_renderer.mm",
+ "godot_view_gesture_recognizer.m",
]
env_ios = env.Clone()
diff --git a/platform/iphone/godot_view.mm b/platform/iphone/godot_view.mm
index cd17ff5430..c0a31549c4 100644
--- a/platform/iphone/godot_view.mm
+++ b/platform/iphone/godot_view.mm
@@ -33,6 +33,7 @@
#include "core/ustring.h"
#import "display_layer.h"
#include "display_server_iphone.h"
+#import "godot_view_gesture_recognizer.h"
#import "godot_view_renderer.h"
#import <CoreMotion/CoreMotion.h>
@@ -57,6 +58,8 @@ static const int max_touches = 8;
@property(strong, nonatomic) CMMotionManager *motionManager;
+@property(strong, nonatomic) GodotViewGestureRecognizer *delayGestureRecognizer;
+
@end
@implementation GodotView
@@ -139,6 +142,10 @@ static const int max_touches = 8;
self.animationTimer = nil;
}
+ if (self.delayGestureRecognizer) {
+ self.delayGestureRecognizer = nil;
+ }
+
[super dealloc];
}
@@ -157,6 +164,12 @@ static const int max_touches = 8;
self.motionManager = nil;
}
}
+
+ // Initialize delay gesture recognizer
+ GodotViewGestureRecognizer *gestureRecognizer = [[GodotViewGestureRecognizer alloc] init];
+ self.delayGestureRecognizer = gestureRecognizer;
+ [self addGestureRecognizer:self.delayGestureRecognizer];
+ [gestureRecognizer release];
}
- (void)stopRendering {
@@ -359,9 +372,6 @@ static const int max_touches = 8;
for (unsigned int i = 0; i < [tlist count]; i++) {
if ([touchesSet containsObject:[tlist objectAtIndex:i]]) {
UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseBegan) {
- continue;
- }
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
@@ -375,9 +385,6 @@ static const int max_touches = 8;
for (unsigned int i = 0; i < [tlist count]; i++) {
if ([touches containsObject:[tlist objectAtIndex:i]]) {
UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseMoved) {
- continue;
- }
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
CGPoint touchPoint = [touch locationInView:self];
@@ -392,9 +399,6 @@ static const int max_touches = 8;
for (unsigned int i = 0; i < [tlist count]; i++) {
if ([touches containsObject:[tlist objectAtIndex:i]]) {
UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseEnded) {
- continue;
- }
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
[self removeTouch:touch];
@@ -409,9 +413,6 @@ static const int max_touches = 8;
for (unsigned int i = 0; i < [tlist count]; i++) {
if ([touches containsObject:[tlist objectAtIndex:i]]) {
UITouch *touch = [tlist objectAtIndex:i];
- if (touch.phase != UITouchPhaseCancelled) {
- continue;
- }
int tid = [self getTouchIDForTouch:touch];
ERR_FAIL_COND(tid == -1);
DisplayServerIPhone::get_singleton()->touches_cancelled(tid);
diff --git a/platform/iphone/godot_view_gesture_recognizer.h b/platform/iphone/godot_view_gesture_recognizer.h
new file mode 100644
index 0000000000..ca3bd808d1
--- /dev/null
+++ b/platform/iphone/godot_view_gesture_recognizer.h
@@ -0,0 +1,44 @@
+/*************************************************************************/
+/* godot_view_gesture_recognizer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+// GLViewGestureRecognizer allows iOS gestures to work currectly by
+// emulating UIScrollView's UIScrollViewDelayedTouchesBeganGestureRecognizer.
+// It catches all gestures incoming to UIView and delays them for 150ms
+// (the same value used by UIScrollViewDelayedTouchesBeganGestureRecognizer)
+// If touch cancelation or end message is fired it fires delayed
+// begin touch immediately as well as last touch signal
+
+#import <UIKit/UIKit.h>
+
+@interface GodotViewGestureRecognizer : UIGestureRecognizer
+
+- (instancetype)init;
+
+@end
diff --git a/platform/iphone/godot_view_gesture_recognizer.m b/platform/iphone/godot_view_gesture_recognizer.m
new file mode 100644
index 0000000000..377ccd52a5
--- /dev/null
+++ b/platform/iphone/godot_view_gesture_recognizer.m
@@ -0,0 +1,171 @@
+/*************************************************************************/
+/* godot_view_gesture_recognizer.m */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#import "godot_view_gesture_recognizer.h"
+
+// Using same delay interval that is used for `UIScrollView`
+const NSTimeInterval kGLGestureDelayInterval = 0.150;
+
+// Minimum distance for touches to move to fire
+// a delay timer before scheduled time.
+// Should be the low enough to not cause issues with dragging
+// but big enough to allow click to work.
+const CGFloat kGLGestureMovementDistance = 0.5;
+
+@interface GodotViewGestureRecognizer ()
+
+// Timer used to delay begin touch message.
+// Should work as simple emulation of UIDelayedAction
+@property(strong, nonatomic) NSTimer *delayTimer;
+
+// Delayed touch parameters
+@property(strong, nonatomic) NSSet *delayedTouches;
+@property(strong, nonatomic) UIEvent *delayedEvent;
+
+@end
+
+@implementation GodotViewGestureRecognizer
+
+- (instancetype)init {
+ self = [super init];
+
+ self.cancelsTouchesInView = YES;
+ self.delaysTouchesBegan = YES;
+ self.delaysTouchesEnded = YES;
+
+ return self;
+}
+
+- (void)dealloc {
+ if (self.delayTimer) {
+ [self.delayTimer invalidate];
+ self.delayTimer = nil;
+ }
+
+ if (self.delayedTouches) {
+ self.delayedTouches = nil;
+ }
+
+ if (self.delayedEvent) {
+ self.delayedEvent = nil;
+ }
+
+ [super dealloc];
+}
+
+- (void)delayTouches:(NSSet *)touches andEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+
+ self.delayedTouches = touches;
+ self.delayedEvent = event;
+
+ self.delayTimer = [NSTimer
+ scheduledTimerWithTimeInterval:kGLGestureDelayInterval
+ target:self
+ selector:@selector(fireDelayedTouches:)
+ userInfo:nil
+ repeats:NO];
+}
+
+- (void)fireDelayedTouches:(id)timer {
+ [self.delayTimer invalidate];
+ self.delayTimer = nil;
+
+ if (self.delayedTouches) {
+ [self.view touchesBegan:self.delayedTouches withEvent:self.delayedEvent];
+ }
+
+ self.delayedTouches = nil;
+ self.delayedEvent = nil;
+}
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseBegan];
+ [self delayTouches:cleared andEvent:event];
+ [cleared release];
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseMoved];
+
+ if (self.delayTimer) {
+ // We should check if movement was significant enough to fire an event
+ // for dragging to work correctly.
+ for (UITouch *touch in cleared) {
+ CGPoint from = [touch locationInView:self.view];
+ CGPoint to = [touch previousLocationInView:self.view];
+ CGFloat xDistance = from.x - to.x;
+ CGFloat yDistance = from.y - to.y;
+
+ CGFloat distance = sqrt(xDistance * xDistance + yDistance * yDistance);
+
+ // Early exit, since one of touches has moved enough to fire a drag event.
+ if (distance > kGLGestureMovementDistance) {
+ [self.delayTimer fire];
+ [self.view touchesMoved:cleared withEvent:event];
+ [cleared release];
+ return;
+ }
+ }
+
+ [cleared release];
+ return;
+ }
+
+ [self.view touchesMoved:cleared withEvent:event];
+ [cleared release];
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+
+ NSSet *cleared = [self copyClearedTouches:touches phase:UITouchPhaseEnded];
+ [self.view touchesEnded:cleared withEvent:event];
+ [cleared release];
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
+ [self.delayTimer fire];
+ [self.view touchesCancelled:touches withEvent:event];
+};
+
+- (NSSet *)copyClearedTouches:(NSSet *)touches phase:(UITouchPhase)phaseToSave {
+ NSMutableSet *cleared = [touches mutableCopy];
+
+ for (UITouch *touch in touches) {
+ if (touch.phase != phaseToSave) {
+ [cleared removeObject:touch];
+ }
+ }
+
+ return cleared;
+}
+
+@end