Skip to content

Commit

Permalink
Merge pull request #559 from brightredchilli/ying/twofingertap
Browse files Browse the repository at this point in the history
Added two finger tap gesture
  • Loading branch information
phatmann committed Feb 16, 2015
2 parents 7178653 + 6f858f0 commit a98d410
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Additions/UIView-KIFAdditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

#import <UIKit/UIKit.h>

extern double KIFDegreesToRadians(double deg);
extern double KIFRadiansToDegrees(double rad);

typedef CGPoint KIFDisplacement;

@interface UIView (KIFAdditions)
Expand Down Expand Up @@ -36,6 +39,7 @@ typedef CGPoint KIFDisplacement;
- (void)flash;
- (void)tap;
- (void)tapAtPoint:(CGPoint)point;
- (void)twoFingerTapAtPoint:(CGPoint)point;
- (void)longPressAtPoint:(CGPoint)point duration:(NSTimeInterval)duration;

/*!
Expand All @@ -52,6 +56,7 @@ typedef CGPoint KIFDisplacement;
- (void)twoFingerPanFromPoint:(CGPoint)startPoint toPoint:(CGPoint)toPoint steps:(NSUInteger)stepCount;
- (void)pinchAtPoint:(CGPoint)centerPoint distance:(CGFloat)distance steps:(NSUInteger)stepCount;
- (void)zoomAtPoint:(CGPoint)centerPoint distance:(CGFloat)distance steps:(NSUInteger)stepCount;
- (void)twoFingerRotateAtPoint:(CGPoint)centerPoint angle:(CGFloat)angleInDegrees;
/*!
@method isTappableWithHitTestResultView:
@abstract Easy hook to override whether a hit test result makes a view tappable.
Expand Down
54 changes: 54 additions & 0 deletions Additions/UIView-KIFAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@
#import "UITouch-KIFAdditions.h"
#import <objc/runtime.h>

double KIFDegreesToRadians(double deg) {
return (deg) / 180.0 * M_PI;
}

double KIFRadiansToDegrees(double rad) {
return ((rad) * (180.0 / M_PI));
}

typedef struct __GSEvent * GSEventRef;

static CGFloat const kTwoFingerConstantWidth = 40;
Expand Down Expand Up @@ -403,6 +411,23 @@ - (void)tapAtPoint:(CGPoint)point;

}

- (void)twoFingerTapAtPoint:(CGPoint)point {
CGPoint finger1 = CGPointMake(point.x - kTwoFingerConstantWidth, point.y - kTwoFingerConstantWidth);
CGPoint finger2 = CGPointMake(point.x + kTwoFingerConstantWidth, point.y + kTwoFingerConstantWidth);
UITouch *touch1 = [[UITouch alloc] initAtPoint:finger1 inView:self];
UITouch *touch2 = [[UITouch alloc] initAtPoint:finger2 inView:self];
[touch1 setPhaseAndUpdateTimestamp:UITouchPhaseBegan];
[touch2 setPhaseAndUpdateTimestamp:UITouchPhaseBegan];

UIEvent *event = [self eventWithTouches:@[touch1, touch2]];
[[UIApplication sharedApplication] sendEvent:event];

[touch1 setPhaseAndUpdateTimestamp:UITouchPhaseEnded];
[touch2 setPhaseAndUpdateTimestamp:UITouchPhaseEnded];

[[UIApplication sharedApplication] sendEvent:event];
}

#define DRAG_TOUCH_DELAY 0.01

- (void)longPressAtPoint:(CGPoint)point duration:(NSTimeInterval)duration
Expand Down Expand Up @@ -580,6 +605,35 @@ - (void)zoomAtPoint:(CGPoint)centerPoint distance:(CGFloat)distance steps:(NSUIn
[self dragPointsAlongPaths:paths];
}

- (void)twoFingerRotateAtPoint:(CGPoint)centerPoint angle:(CGFloat)angleInDegrees {
NSInteger stepCount = ABS(angleInDegrees)/2; // very rough approximation. 90deg = ~45 steps, 360 deg = ~180 steps
CGFloat radius = kTwoFingerConstantWidth*2;
double angleInRadians = KIFDegreesToRadians(angleInDegrees);

NSMutableArray *finger1Path = [NSMutableArray array];
NSMutableArray *finger2Path = [NSMutableArray array];
for (NSUInteger i = 0; i < stepCount; i++) {
double currentAngle = 0;
if (i == stepCount - 1) {
currentAngle = angleInRadians; // do not interpolate for the last step for maximum accuracy
}
else {
double interpolation = i/(double)stepCount;
currentAngle = interpolation * angleInRadians;
}
// interpolate betwen 0 and the target rotation
CGPoint offset1 = CGPointMake(radius * cos(currentAngle), radius * sin(currentAngle));
CGPoint offset2 = CGPointMake(-offset1.x, -offset1.y); // second finger is just opposite of the first

CGPoint finger1 = CGPointMake(centerPoint.x + offset1.x, centerPoint.y + offset1.y);
CGPoint finger2 = CGPointMake(centerPoint.x + offset2.x, centerPoint.y + offset2.y);

[finger1Path addObject:[NSValue valueWithCGPoint:finger1]];
[finger2Path addObject:[NSValue valueWithCGPoint:finger2]];
}
[self dragPointsAlongPaths:@[[finger1Path copy], [finger2Path copy]]];
}

- (NSArray *)pointsFromStartPoint:(CGPoint)startPoint toPoint:(CGPoint)toPoint steps:(NSUInteger)stepCount {

CGPoint displacement = CGPointMake(toPoint.x - startPoint.x, toPoint.y - startPoint.y);
Expand Down
59 changes: 59 additions & 0 deletions KIF Tests/MultiFingerTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
#import <KIF/UIApplication-KIFAdditions.h>

@interface MultiFingerTests : KIFTestCase
@property (nonatomic, readwrite) BOOL twoFingerTapSuccess;
@property (nonatomic, readwrite) BOOL twoFingerPanSuccess;
@property (nonatomic, readwrite) BOOL zoomSuccess;
@property (nonatomic, readwrite) double latestRotation;
@end

@implementation MultiFingerTests
Expand All @@ -25,15 +27,36 @@ - (void)beforeEach
UIScrollView *scrollView = (UIScrollView *)[tester waitForViewWithAccessibilityLabel:@"Scroll View"];
scrollView.contentOffset = CGPointZero;

self.twoFingerTapSuccess = NO;
self.twoFingerPanSuccess = NO;
self.zoomSuccess = NO;
self.latestRotation = 0;
}

- (void)afterEach
{
[tester tapViewWithAccessibilityLabel:@"Test Suite" traits:UIAccessibilityTraitButton];
self.twoFingerTapSuccess = NO;
self.twoFingerPanSuccess = NO;
self.zoomSuccess = NO;
self.latestRotation = 0;
}

- (void)testTwoFingerTap {
UIScrollView *scrollView = (UIScrollView *)[tester waitForViewWithAccessibilityLabel:@"Scroll View"];
UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(twoFingerTapped)];
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[scrollView addGestureRecognizer:twoFingerTapRecognizer];

[scrollView twoFingerTapAtPoint:CGPointMake(CGRectGetMidX(scrollView.bounds), CGRectGetMidY(scrollView.bounds))];

__KIFAssertEqual(self.twoFingerTapSuccess, YES);
[scrollView removeGestureRecognizer:twoFingerTapRecognizer];
}

- (void)twoFingerTapped {
self.twoFingerTapSuccess = YES;
}

- (void)testTwoFingerPan
Expand All @@ -50,6 +73,7 @@ - (void)testTwoFingerPan
[scrollView twoFingerPanFromPoint:startPoint toPoint:endPoint steps:10];

__KIFAssertEqual(self.twoFingerPanSuccess, YES);
[scrollView removeGestureRecognizer:panGestureRecognizer];
}

- (void)twoFingerPanned {
Expand All @@ -69,6 +93,7 @@ - (void)testZoom {
[scrollView zoomAtPoint:startPoint distance:distance steps:10];

__KIFAssertEqual(self.zoomSuccess, YES);
[scrollView removeGestureRecognizer:pinchRecognizer];
}

- (void)zoomed:(UIPinchGestureRecognizer *)pinchRecognizer {
Expand All @@ -83,4 +108,38 @@ - (void)zoomed:(UIPinchGestureRecognizer *)pinchRecognizer {

}

- (void)testRotate {
UIScrollView *scrollView = (UIScrollView *)[tester waitForViewWithAccessibilityLabel:@"Scroll View"];
UIRotationGestureRecognizer *rotateRecognizer =
[[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotated:)];

[scrollView addGestureRecognizer:rotateRecognizer];

[self assertThatLatestRotationIsWithinThreshold:45];
[self assertThatLatestRotationIsWithinThreshold:90];
[self assertThatLatestRotationIsWithinThreshold:180];
[self assertThatLatestRotationIsWithinThreshold:270];
[self assertThatLatestRotationIsWithinThreshold:360];

[scrollView removeGestureRecognizer:rotateRecognizer];
}

- (void)assertThatLatestRotationIsWithinThreshold:(double)targetRotationInDegrees {
UIScrollView *scrollView = (UIScrollView *)[tester waitForViewWithAccessibilityLabel:@"Scroll View"];
CGPoint startPoint = CGPointMake(CGRectGetMidX(scrollView.bounds), CGRectGetMidY(scrollView.bounds));
[scrollView twoFingerRotateAtPoint:startPoint angle:targetRotationInDegrees];

// check we have rotated to within some small threshold of the target rotation amount
// 0.2 radians is ~12 degrees
BOOL withinThreshold = (self.latestRotation - KIFDegreesToRadians(targetRotationInDegrees)) < 0.2;
__KIFAssertEqual(withinThreshold, YES);
}

- (void)rotated:(UIRotationGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded)
{
self.latestRotation = recognizer.rotation;
}
}

@end

0 comments on commit a98d410

Please sign in to comment.