diff --git a/apidoc/Titanium/UI/NavigationWindow.yml b/apidoc/Titanium/UI/NavigationWindow.yml index b173599898e..83e9098e85d 100644 --- a/apidoc/Titanium/UI/NavigationWindow.yml +++ b/apidoc/Titanium/UI/NavigationWindow.yml @@ -31,6 +31,16 @@ properties: availability: creation optional: false + - name: interactiveDismissModeEnabled + summary: | + A boolean indicating whether or not child windows of this navigation window + should have the ability to be swipe-to-closed over the full width of it's window or not. + type: Boolean + default: false + platforms: [iphone, ipad, macos] + since: "12.5.0" + availability: creation + methods: - name: closeWindow summary: Closes a window and removes it from the navigation window. diff --git a/apidoc/Titanium/UI/TabGroup.yml b/apidoc/Titanium/UI/TabGroup.yml index c789b8a7624..66cbf4c1d4b 100644 --- a/apidoc/Titanium/UI/TabGroup.yml +++ b/apidoc/Titanium/UI/TabGroup.yml @@ -609,6 +609,16 @@ properties: platforms: [android, iphone, ipad, macos] since: "12.1.0" + - name: interactiveDismissModeEnabled + summary: | + A boolean indicating whether or not child windows of this tab group + should have the ability to be swipe-to-closed over the full width of it's window or not. + type: Boolean + default: false + platforms: [iphone, ipad, macos] + since: "12.5.0" + availability: creation + examples: - title: Alloy XML Markup example: | diff --git a/iphone/Classes/TiUINavigationWindowProxy.h b/iphone/Classes/TiUINavigationWindowProxy.h index f5777d15981..f7324480351 100644 --- a/iphone/Classes/TiUINavigationWindowProxy.h +++ b/iphone/Classes/TiUINavigationWindowProxy.h @@ -16,6 +16,8 @@ TiWindowProxy *current; BOOL transitionIsAnimating; BOOL transitionWithGesture; + + UIPanGestureRecognizer *fullWidthBackGestureRecognizer; } // Private API diff --git a/iphone/Classes/TiUINavigationWindowProxy.m b/iphone/Classes/TiUINavigationWindowProxy.m index d6f2f9ac4c2..1f61acce45d 100644 --- a/iphone/Classes/TiUINavigationWindowProxy.m +++ b/iphone/Classes/TiUINavigationWindowProxy.m @@ -16,9 +16,16 @@ @implementation TiUINavigationWindowProxy - (void)_destroy { + if (fullWidthBackGestureRecognizer != nil) { + [fullWidthBackGestureRecognizer setDelegate:nil]; + [navController.view removeGestureRecognizer:fullWidthBackGestureRecognizer]; + } + RELEASE_TO_NIL(rootWindow); RELEASE_TO_NIL(navController); RELEASE_TO_NIL(current); + RELEASE_TO_NIL(fullWidthBackGestureRecognizer); + [super _destroy]; } @@ -88,14 +95,45 @@ - (UINavigationController *)controller [TiUtils configureController:navController withObject:self]; [navController.interactivePopGestureRecognizer addTarget:self action:@selector(popGestureStateHandler:)]; [[navController interactivePopGestureRecognizer] setDelegate:self]; + + BOOL interactiveDismissModeEnabled = [TiUtils boolValue:[self valueForKey:@"interactiveDismissModeEnabled"] def:NO]; + if (interactiveDismissModeEnabled) { + [self configureFullWidthSwipeToClose]; + } } return navController; } +- (void)configureFullWidthSwipeToClose +{ + fullWidthBackGestureRecognizer = [[UIPanGestureRecognizer alloc] init]; + + if (navController.interactivePopGestureRecognizer == nil) { + return; + } + + id targets = [navController.interactivePopGestureRecognizer valueForKey:@"targets"]; + if (targets == nil) { + return; + } + + [fullWidthBackGestureRecognizer setValue:targets forKey:@"targets"]; + [fullWidthBackGestureRecognizer setDelegate:self]; + [navController.view addGestureRecognizer:fullWidthBackGestureRecognizer]; +} + - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { BOOL isRootWindow = (current == rootWindow); + BOOL interactiveDismissModeEnabled = [TiUtils boolValue:[self valueForKey:@"interactiveDismissModeEnabled"] def:NO]; + if (interactiveDismissModeEnabled && [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { + BOOL isSystemSwipeToCloseEnabled = navController.interactivePopGestureRecognizer.isEnabled == YES; + BOOL areThereStackedViewControllers = navController.viewControllers.count > 1; + + return isSystemSwipeToCloseEnabled || areThereStackedViewControllers; + } + if (current != nil && !isRootWindow) { return [TiUtils boolValue:[current valueForKey:@"swipeToClose"] def:YES]; } diff --git a/iphone/Classes/TiUITabProxy.h b/iphone/Classes/TiUITabProxy.h index f44e5dadb20..f4ef08abe18 100644 --- a/iphone/Classes/TiUITabProxy.h +++ b/iphone/Classes/TiUITabProxy.h @@ -30,6 +30,8 @@ BOOL activeIconOriginal; id parentOrientationController; + + UIPanGestureRecognizer *fullWidthBackGestureRecognizer; } - (void)setTabGroup:(TiUITabGroupProxy *)proxy; diff --git a/iphone/Classes/TiUITabProxy.m b/iphone/Classes/TiUITabProxy.m index c14e0ff3ce0..4f33b856b89 100644 --- a/iphone/Classes/TiUITabProxy.m +++ b/iphone/Classes/TiUITabProxy.m @@ -32,6 +32,11 @@ - (void)_destroy { [[NSNotificationCenter defaultCenter] removeObserver:self name:kTiTraitCollectionChanged object:nil]; + if (fullWidthBackGestureRecognizer != nil) { + [fullWidthBackGestureRecognizer setDelegate:nil]; + [controller.view removeGestureRecognizer:fullWidthBackGestureRecognizer]; + } + if (rootWindow != nil) { [self cleanNavStack:YES]; } @@ -39,6 +44,8 @@ - (void)_destroy RELEASE_TO_NIL(rootWindow); RELEASE_TO_NIL(controller); RELEASE_TO_NIL(current); + RELEASE_TO_NIL(fullWidthBackGestureRecognizer); + [super _destroy]; } @@ -260,12 +267,43 @@ - (UINavigationController *)controller [controllerStack addObject:[self rootController]]; [controller.interactivePopGestureRecognizer addTarget:self action:@selector(popGestureStateHandler:)]; [[controller interactivePopGestureRecognizer] setDelegate:self]; + + BOOL interactiveDismissModeEnabled = [TiUtils boolValue:[tabGroup valueForKey:@"interactiveDismissModeEnabled"] def:NO]; + if (interactiveDismissModeEnabled) { + [self configureFullWidthSwipeToClose]; + } } return controller; } +- (void)configureFullWidthSwipeToClose +{ + fullWidthBackGestureRecognizer = [[UIPanGestureRecognizer alloc] init]; + + if (controller.interactivePopGestureRecognizer == nil) { + return; + } + + id targets = [controller.interactivePopGestureRecognizer valueForKey:@"targets"]; + if (targets == nil) { + return; + } + + [fullWidthBackGestureRecognizer setValue:targets forKey:@"targets"]; + [fullWidthBackGestureRecognizer setDelegate:self]; + [controller.view addGestureRecognizer:fullWidthBackGestureRecognizer]; +} + - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { + BOOL interactiveDismissModeEnabled = [TiUtils boolValue:[self valueForKey:@"interactiveDismissModeEnabled"] def:NO]; + if (interactiveDismissModeEnabled && [gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) { + BOOL isSystemSwipeToCloseEnabled = controller.interactivePopGestureRecognizer.isEnabled == YES; + BOOL areThereStackedViewControllers = controller.viewControllers.count > 1; + + return isSystemSwipeToCloseEnabled || areThereStackedViewControllers; + } + if (current != nil) { return [TiUtils boolValue:[current valueForKey:@"swipeToClose"] def:YES]; }