diff --git a/KIF Tests/GestureTests.m b/KIF Tests/GestureTests.m index ecb62b62..1c7abf8e 100644 --- a/KIF Tests/GestureTests.m +++ b/KIF Tests/GestureTests.m @@ -193,6 +193,34 @@ - (void)testSwipingDownWithIdentifier [tester waitForViewWithAccessibilityLabel:@"Down"]; } +- (void)testSwipingFromScreenEdgeLeft +{ + UIView *view = [tester waitForViewWithAccessibilityIdentifier:@"gestures.swipeMe"]; + CGSize windowSize = view.window.bounds.size; + CGPoint point = CGPointMake(0.5, 200); + point = [view convertPoint:point fromView:view.window]; + KIFDisplacement displacement = CGPointMake(windowSize.width * 0.5, 5); + [view dragFromPoint:point displacement:displacement steps:20]; + [tester waitForAbsenceOfViewWithAccessibilityLabel:@"LeftEdge"]; + + [tester swipeFromEdge:UIRectEdgeLeft]; + [tester waitForViewWithAccessibilityLabel:@"LeftEdge"]; +} + +- (void)testSwipingFromScreenEdgeRight +{ + UIView *view = [tester waitForViewWithAccessibilityIdentifier:@"gestures.swipeMe"]; + CGSize windowSize = view.window.bounds.size; + CGPoint point = CGPointMake(windowSize.width - 0.5, 200); + point = [view convertPoint:point fromView:view.window]; + KIFDisplacement displacement = CGPointMake(-windowSize.width * 0.5, 5); + [view dragFromPoint:point displacement:displacement steps:20]; + [tester waitForAbsenceOfViewWithAccessibilityLabel:@"RightEdge"]; + + [tester swipeFromEdge:UIRectEdgeRight]; + [tester waitForViewWithAccessibilityLabel:@"RightEdge"]; +} + - (void)testScrolling { // Needs to be offset from the edge to prevent the navigation controller's interactivePopGestureRecognizer from triggering diff --git a/KIF Tests/GestureTests_ViewTestActor.m b/KIF Tests/GestureTests_ViewTestActor.m index d34fe93e..43e303b0 100644 --- a/KIF Tests/GestureTests_ViewTestActor.m +++ b/KIF Tests/GestureTests_ViewTestActor.m @@ -108,4 +108,16 @@ - (void)testMissingScrollableElement KIFExpectFailure([[[viewTester usingTimeout:0.25] usingIdentifier:@"Unknown"] scrollByFractionOfSizeHorizontal:0.5 vertical:0.5]); } +- (void)testSwipingFromScreenEdgeLeft +{ + [viewTester swipeFromEdge:UIRectEdgeLeft]; + [[viewTester usingLabel:@"LeftEdge"] waitForView]; +} + +- (void)testSwipingFromScreenEdgeRight +{ + [viewTester swipeFromEdge:UIRectEdgeRight]; + [[viewTester usingLabel:@"RightEdge"] waitForView]; +} + @end diff --git a/Sources/KIF/Additions/UITouch-KIFAdditions.h b/Sources/KIF/Additions/UITouch-KIFAdditions.h index ea8dc350..5fedffe4 100644 --- a/Sources/KIF/Additions/UITouch-KIFAdditions.h +++ b/Sources/KIF/Additions/UITouch-KIFAdditions.h @@ -17,5 +17,6 @@ - (void)setLocationInWindow:(CGPoint)location; - (void)setPhaseAndUpdateTimestamp:(UITouchPhase)phase; +- (void)setIsFromEdge:(BOOL)isFromEdge; @end diff --git a/Sources/KIF/Additions/UITouch-KIFAdditions.m b/Sources/KIF/Additions/UITouch-KIFAdditions.m index 59b2e34e..9cc9880c 100644 --- a/Sources/KIF/Additions/UITouch-KIFAdditions.m +++ b/Sources/KIF/Additions/UITouch-KIFAdditions.m @@ -37,6 +37,7 @@ - (void)_setIsFirstTouchForView:(BOOL)firstTouchForView; - (void)_setIsTapToClick:(BOOL)tapToClick; - (void)_setHidEvent:(IOHIDEventRef)event; +- (void)_setEdgeType:(NSInteger)edgeType; @end @@ -117,6 +118,12 @@ - (void)setPhaseAndUpdateTimestamp:(UITouchPhase)phase [self setPhase:phase]; } +- (void)setIsFromEdge:(BOOL)isFromEdge +{ + NSInteger edgeType = isFromEdge ? 4 : 0; + [self _setEdgeType:edgeType]; +} + - (void)kif_setHidEvent { IOHIDEventRef event = kif_IOHIDEventWithTouches(@[self]); [self _setHidEvent:event]; diff --git a/Sources/KIF/Additions/UIView-KIFAdditions.h b/Sources/KIF/Additions/UIView-KIFAdditions.h index acb48187..31995d81 100644 --- a/Sources/KIF/Additions/UIView-KIFAdditions.h +++ b/Sources/KIF/Additions/UIView-KIFAdditions.h @@ -53,6 +53,7 @@ typedef CGPoint KIFDisplacement; - (void)dragFromPoint:(CGPoint)startPoint toPoint:(CGPoint)endPoint; - (void)dragFromPoint:(CGPoint)startPoint toPoint:(CGPoint)endPoint steps:(NSUInteger)stepCount; - (void)dragFromPoint:(CGPoint)startPoint displacement:(KIFDisplacement)displacement steps:(NSUInteger)stepCount; +- (void)dragFromEdge:(UIRectEdge)startEdge toEdge:(UIRectEdge)endEdge; - (void)dragAlongPathWithPoints:(CGPoint *)points count:(NSInteger)count; - (void)twoFingerPanFromPoint:(CGPoint)startPoint toPoint:(CGPoint)toPoint steps:(NSUInteger)stepCount; - (void)pinchAtPoint:(CGPoint)centerPoint distance:(CGFloat)distance steps:(NSUInteger)stepCount; diff --git a/Sources/KIF/Additions/UIView-KIFAdditions.m b/Sources/KIF/Additions/UIView-KIFAdditions.m index 4d7b31c3..93bec407 100644 --- a/Sources/KIF/Additions/UIView-KIFAdditions.m +++ b/Sources/KIF/Additions/UIView-KIFAdditions.m @@ -636,6 +636,27 @@ - (void)dragFromPoint:(CGPoint)startPoint displacement:(KIFDisplacement)displace [self dragPointsAlongPaths:@[path]]; } +- (void)dragFromEdge:(UIRectEdge)startEdge toEdge:(UIRectEdge)endEdge +{ + CGFloat width = self.bounds.size.width; + CGFloat height = self.bounds.size.height; + CGFloat edgeInset = 0.5; + NSDictionary *edgeToPoint = @{ + @(UIRectEdgeTop): @(CGPointMake(width / 2, edgeInset)), + @(UIRectEdgeLeft): @(CGPointMake(edgeInset, height / 2)), + @(UIRectEdgeBottom): @(CGPointMake(width / 2, height - edgeInset)), + @(UIRectEdgeRight): @(CGPointMake(width - edgeInset, height / 2)), + }; + CGPoint startPoint = [edgeToPoint[@(startEdge)] CGPointValue]; + CGPoint endPoint = [edgeToPoint[@(endEdge)] CGPointValue]; + + CGPoint screenPoint = [self convertPoint:startPoint toView:self.window]; + BOOL isFromScreenEdge = (screenPoint.x < 1 || screenPoint.x > self.window.bounds.size.width - 1); + + NSArray *path = [self pointsFromStartPoint:startPoint toPoint:endPoint steps:20]; + [self dragPointsAlongPaths:@[path] isFromEdge:isFromScreenEdge]; +} + - (void)dragAlongPathWithPoints:(CGPoint *)points count:(NSInteger)count; { // convert point array into NSArray with NSValue @@ -648,6 +669,10 @@ - (void)dragAlongPathWithPoints:(CGPoint *)points count:(NSInteger)count; } - (void)dragPointsAlongPaths:(NSArray *> *)arrayOfPaths { + [self dragPointsAlongPaths:arrayOfPaths isFromEdge:NO]; +} + +- (void)dragPointsAlongPaths:(NSArray *> *)arrayOfPaths isFromEdge:(BOOL)isFromEdge { // There must be at least one path with at least one point if (arrayOfPaths.count == 0 || arrayOfPaths.firstObject.count == 0) { @@ -692,6 +717,7 @@ - (void)dragPointsAlongPaths:(NSArray *> *)arrayOfPaths { point = [self convertPoint:point fromView:self.window]; UITouch *touch = [[UITouch alloc] initAtPoint:point inView:self]; [touch setPhaseAndUpdateTimestamp:UITouchPhaseBegan]; + [touch setIsFromEdge:isFromEdge]; [touches addObject:touch]; } UIEvent *eventDown = [self eventWithTouches:[NSArray arrayWithArray:touches]]; diff --git a/Sources/KIF/Classes/KIFUITestActor.h b/Sources/KIF/Classes/KIFUITestActor.h index db346ed0..17dd4a39 100644 --- a/Sources/KIF/Classes/KIFUITestActor.h +++ b/Sources/KIF/Classes/KIFUITestActor.h @@ -322,6 +322,12 @@ typedef NS_ENUM(NSUInteger, KIFPullToRefreshTiming) { */ - (void)tapScreenAtPoint:(CGPoint)screenPoint; +/*! + @abstract Performs a swipe gesture starting from the specified edge of the screen. + @param edge The edge from which the swipe gesture should start. + */ +- (void)swipeFromEdge:(UIRectEdge)edge; + /*! @abstract Performs a long press on a particular view in the view hierarchy. @discussion The view or accessibility element with the given label is searched for in the view hierarchy. If the element isn't found or isn't currently tappable, then the step will attempt to wait until it is. Once the view is present and tappable, touch events are simulated in the center of the view or element. diff --git a/Sources/KIF/Classes/KIFUITestActor.m b/Sources/KIF/Classes/KIFUITestActor.m index ff8c9ca1..8b753ae2 100644 --- a/Sources/KIF/Classes/KIFUITestActor.m +++ b/Sources/KIF/Classes/KIFUITestActor.m @@ -366,18 +366,7 @@ - (void)tapAccessibilityElement:(UIAccessibilityElement *)element inView:(UIView - (void)tapScreenAtPoint:(CGPoint)screenPoint { [self runBlock:^KIFTestStepResult(NSError **error) { - - // Try all the windows until we get one back that actually has something in it at the given point - UIView *view = nil; - for (UIWindow *window in [[[UIApplication sharedApplication] windowsWithKeyWindow] reverseObjectEnumerator]) { - CGPoint windowPoint = [window convertPoint:screenPoint fromView:nil]; - view = [window hitTest:windowPoint withEvent:nil]; - - // If we hit the window itself, then skip it. - if (view != window && view != nil) { - break; - } - } + UIView *view = [self viewAtPoint:screenPoint]; KIFTestWaitCondition(view, error, @"No view was found at the point %@", NSStringFromCGPoint(screenPoint)); @@ -389,6 +378,45 @@ - (void)tapScreenAtPoint:(CGPoint)screenPoint }]; } +- (void)swipeFromEdge:(UIRectEdge)edge +{ + CGSize screenSize = UIScreen.mainScreen.bounds.size; + CGPoint screenPoint; + if (edge == UIRectEdgeLeft) { + screenPoint = CGPointMake(0.3, screenSize.height / 2); + } else if (edge == UIRectEdgeRight) { + screenPoint = CGPointMake(screenSize.width - 0.3, screenSize.height / 2); + } else { + return; + } + [self runBlock:^KIFTestStepResult(NSError **error) { + UIView *view = [self viewAtPoint:screenPoint]; + + KIFTestWaitCondition(view, error, @"No view was found at the point %@", NSStringFromCGPoint(screenPoint)); + + UIRectEdge endEdge = (UIRectEdgeLeft | UIRectEdgeRight) - edge; + [view dragFromEdge:edge toEdge:endEdge]; + + return KIFTestStepResultSuccess; + }]; +} + +- (UIView *)viewAtPoint:(CGPoint)screenPoint +{ + // Try all the windows until we get one back that actually has something in it at the given point + UIView *view = nil; + for (UIWindow *window in [[[UIApplication sharedApplication] windowsWithKeyWindow] reverseObjectEnumerator]) { + CGPoint windowPoint = [window convertPoint:screenPoint fromView:nil]; + view = [window hitTest:windowPoint withEvent:nil]; + + // If we hit the window itself, then skip it. + if (view != window && view != nil) { + break; + } + } + return view; +} + - (void)longPressViewWithAccessibilityLabel:(NSString *)label duration:(NSTimeInterval)duration; { [self longPressViewWithAccessibilityLabel:label value:nil traits:UIAccessibilityTraitNone duration:duration]; diff --git a/Sources/KIF/Classes/KIFUIViewTestActor.h b/Sources/KIF/Classes/KIFUIViewTestActor.h index 1df5d02f..4858ce4e 100644 --- a/Sources/KIF/Classes/KIFUIViewTestActor.h +++ b/Sources/KIF/Classes/KIFUIViewTestActor.h @@ -165,6 +165,12 @@ extern NSString *const inputFieldTestString; */ - (void)swipeInDirection:(KIFSwipeDirection)direction; +/*! + @abstract Performs a swipe gesture starting from the specified edge of the screen. + @param edge The edge from which the swipe gesture should start. + */ +- (void)swipeFromEdge:(UIRectEdge)edge; + #pragma mark Waiting & Finding /*! diff --git a/Sources/KIF/Classes/KIFUIViewTestActor.m b/Sources/KIF/Classes/KIFUIViewTestActor.m index 5039f75a..e27ed576 100644 --- a/Sources/KIF/Classes/KIFUIViewTestActor.m +++ b/Sources/KIF/Classes/KIFUIViewTestActor.m @@ -380,6 +380,11 @@ - (void)swipeInDirection:(KIFSwipeDirection)direction; } } +- (void)swipeFromEdge:(UIRectEdge)edge +{ + [self.actor swipeFromEdge:edge]; +} + #pragma mark - Scroll/Table/CollectionView Actions - (void)scrollByFractionOfSizeHorizontal:(CGFloat)horizontalFraction vertical:(CGFloat)verticalFraction; diff --git a/Test Host/Base.lproj/MainStoryboard.storyboard b/Test Host/Base.lproj/MainStoryboard.storyboard index 1235360b..a0647980 100644 --- a/Test Host/Base.lproj/MainStoryboard.storyboard +++ b/Test Host/Base.lproj/MainStoryboard.storyboard @@ -1248,39 +1248,12 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + @@ -1362,6 +1404,20 @@ + + + + + + + + + + + + + + diff --git a/Test Host/GestureViewController.m b/Test Host/GestureViewController.m index a41f701a..77fd0eda 100644 --- a/Test Host/GestureViewController.m +++ b/Test Host/GestureViewController.m @@ -8,7 +8,7 @@ #import -@interface GestureViewController : UIViewController +@interface GestureViewController : UIViewController @property (weak, nonatomic) IBOutlet UILabel *lastSwipeDescriptionLabel; @property (weak, nonatomic) IBOutlet UILabel *lastVelocityVeluesLabel; @property (weak, nonatomic) IBOutlet UILabel *bottomRightLabel; @@ -26,6 +26,20 @@ - (void)viewDidLoad self.scrollView.contentSize = CGRectUnion(self.scrollView.bounds, self.bottomRightLabel.frame).size; } +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + self.navigationController.interactivePopGestureRecognizer.enabled = NO; +} + +- (void)viewDidDisappear:(BOOL)animated +{ + [super viewDidDisappear:animated]; + + self.navigationController.interactivePopGestureRecognizer.enabled = YES; +} + - (IBAction)swipedUp:(id)sender { self.lastSwipeDescriptionLabel.text = @"Up"; @@ -51,9 +65,24 @@ - (IBAction)hadlePanGestureRecognizer:(UIPanGestureRecognizer *)sender self.lastVelocityVeluesLabel.text = [self formattedVelocityValues:[sender velocityInView:self.panAreaLabel]]; } +- (IBAction)handleScreenEdgePanGestureRecognizer:(UIScreenEdgePanGestureRecognizer *)sender +{ + self.lastSwipeDescriptionLabel.text = sender.edges == UIRectEdgeLeft ? @"LeftEdge" : @"RightEdge"; +} + - (NSString*)formattedVelocityValues:(CGPoint)velocity { return [NSString stringWithFormat:@"X:%.2f Y:%.2f", velocity.x, velocity.y]; } +#pragma mark - UIGestureRecognizerDelegate + +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer +{ + if ([gestureRecognizer isKindOfClass:UIScreenEdgePanGestureRecognizer.class]) { + return YES; + } + return NO; +} + @end