├── .gitignore ├── LICENSE ├── README.md ├── Snapshots └── 1.gif ├── XLPopGesture.podspec ├── XLPopGesture ├── UINavigationController+XLPopGesture.h └── UINavigationController+XLPopGesture.m └── XLPopGestureDemo ├── XLPopGestureDemo.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata └── XLPopGestureDemo ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets ├── AppIcon.appiconset │ └── Contents.json ├── Contents.json ├── tabbar.imageset │ ├── Contents.json │ ├── tabbar@2x.png │ └── tabbar@3x.png └── tabbar_selected.imageset │ ├── Contents.json │ ├── tabbar_selected@2x.png │ └── tabbar_selected@3x.png ├── Base.lproj └── LaunchScreen.storyboard ├── Info.plist ├── XLOneViewController.h ├── XLOneViewController.m └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | .DS_Store 6 | 7 | ## Build generated 8 | build/ 9 | DerivedData/ 10 | 11 | ## Various settings 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata/ 21 | 22 | ## Other 23 | *.moved-aside 24 | *.xcuserstate 25 | 26 | ## Obj-C/Swift specific 27 | *.hmap 28 | *.ipa 29 | *.dSYM.zip 30 | *.dSYM 31 | 32 | # CocoaPods 33 | # 34 | # We recommend against adding the Pods directory to your .gitignore. However 35 | # you should judge for yourself, the pros and cons are mentioned at: 36 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 37 | # 38 | Pods/ 39 | Podfile.lock 40 | 41 | # Carthage 42 | # 43 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 44 | # Carthage/Checkouts 45 | 46 | Carthage/Build 47 | 48 | # fastlane 49 | # 50 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 51 | # screenshots whenever they are needed. 52 | # For more information about the recommended setup visit: 53 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 54 | 55 | fastlane/report.xml 56 | fastlane/Preview.html 57 | fastlane/screenshots 58 | fastlane/test_output 59 | 60 | # Code Injection 61 | # 62 | # After new code Injection tools there's a generated folder /iOSInjectionProject 63 | # https://github.com/johnno1962/injectionforxcode 64 | 65 | iOSInjectionProject/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 cctomato 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XLPopGesture 2 | An UINavigationController's category to enable fullscreen pop gesture in an iOS7+ system style with AOP. 3 | 4 | # Overview 5 | 6 | ![snapshot](/Snapshots/1.gif) 7 | 8 | Design ideas come from [FDFullscreenPopGesture](https://github.com/forkingdog/FDFullscreenPopGesture) and [MVVMReactiveCocoa](https://github.com/leichunfeng/MVVMReactiveCocoa) 9 | 10 | Thanks for sharing. 11 | 12 | # Usage 13 | 14 | AOP, just add 2 files and no need for any setups, all navigation controllers will be able to use fullscreen pop gesture automatically. 15 | 16 | To disable this pop gesture of a view controller: 17 | 18 | ```objc 19 | viewController.xl_prefersDisablePop = YES; 20 | ``` 21 | 22 | To hidden the navigationBar of a view controller: 23 | 24 | ```objc 25 | viewController.xl_prefersNavigationBarHidden = YES; 26 | ``` 27 | 28 | To close the Pop effects of a navigationController: 29 | 30 | ```objc 31 | navigationController.xl_prefersOpenPopEffects = NO; 32 | ``` 33 | 34 | TabbarItem will be hidden when push ViewController,if you do not want this: 35 | 36 | ```objc 37 | navigationController.xl_prefersHiddenTabBar = NO; 38 | ``` 39 | 40 | # Installation 41 | 42 | Use CocoaPods 43 | 44 | ```ruby 45 | pod 'XLPopGesture' 46 | ``` 47 | 48 | #License 49 | 50 | MIT -------------------------------------------------------------------------------- /Snapshots/1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cctomato/XLPopGesture/cf00cbf9d8f0970453df959b7074b6dd5a40c121/Snapshots/1.gif -------------------------------------------------------------------------------- /XLPopGesture.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "XLPopGesture" 3 | s.version = "2.0" 4 | s.summary = "A custom PopGesture with AOP." 5 | s.description = "A custom PopGesture with AOP. Just pod in 2 files and no need for any setups." 6 | s.homepage = "https://github.com/cctomato/XLPopGesture" 7 | 8 | s.license = { :type => "MIT", :file => "LICENSE" } 9 | 10 | s.author = { "cctomato" => "cyq_1103@live.com" } 11 | 12 | s.platform = :ios, "7.0" 13 | 14 | s.source = { :git => "https://github.com/cctomato/XLPopGesture.git", :tag => "#{s.version}" } 15 | 16 | s.source_files = "XLPopGesture/*.{h,m}" 17 | 18 | s.requires_arc = true 19 | 20 | end 21 | -------------------------------------------------------------------------------- /XLPopGesture/UINavigationController+XLPopGesture.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016-2017 cctomato ( https://github.com/cctomato ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import 24 | 25 | @interface UIViewController (XLPopGesture) 26 | /** 27 | *A snapshot view based on the contents of the current view 28 | **/ 29 | @property (nonatomic, strong) UIView *xl_snapshot; 30 | /** 31 | *Default to NO, if you want to hidden navgationBar, Set YES 32 | **/ 33 | @property (nonatomic, assign) BOOL xl_prefersNavigationBarHidden; 34 | /** 35 | *Default to NO, if you want to disable the pop gesture, Set YES 36 | **/ 37 | @property (nonatomic, assign) BOOL xl_prefersDisablePop; 38 | 39 | @end 40 | 41 | @interface UINavigationController (XLPopGesture) 42 | /** 43 | *A view controller is able to control navigation bar's appearance by itself, 44 | *rather than a global way, checking "xl_prefersNavigationBarHidden" property. 45 | *Default to YES, disable it if you don't want so. 46 | **/ 47 | @property (nonatomic, assign) BOOL xl_viewControllerBasedNavigationBarAppearanceEnabled; 48 | /** 49 | *The gesture recognizer that actually handles interactive pop. 50 | **/ 51 | @property (nonatomic, strong, readonly) UIPanGestureRecognizer *xl_popRecoginzer; 52 | /** 53 | *Default to YES, if you do not want to hidden tabBar, set NO 54 | **/ 55 | @property (nonatomic, assign) BOOL xl_prefersHiddenTabBar; 56 | /** 57 | *Default to YES, if you do not want to close the Pop effects, set NO 58 | **/ 59 | @property (nonatomic, assign) BOOL xl_prefersOpenPopEffects; 60 | /** 61 | *The gesture percentage 62 | **/ 63 | @property (nonatomic, strong) UIPercentDrivenInteractiveTransition *xl_interactivePopTransition; 64 | @end 65 | -------------------------------------------------------------------------------- /XLPopGesture/UINavigationController+XLPopGesture.m: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016-2017 cctomato ( https://github.com/cctomato ) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | #import "UINavigationController+XLPopGesture.h" 24 | #import 25 | 26 | @interface XLPopGestureDelegate : NSObject 27 | @property (nonatomic, weak) UINavigationController *navigationController; 28 | @end 29 | 30 | @implementation XLPopGestureDelegate 31 | 32 | - (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer 33 | { 34 | if (self.navigationController.viewControllers.count <= 1) { 35 | return NO; 36 | } 37 | 38 | UIViewController *topViewController = [self.navigationController.viewControllers lastObject]; 39 | if (topViewController.xl_prefersDisablePop) { 40 | return NO; 41 | } 42 | 43 | if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) { 44 | return NO; 45 | } 46 | 47 | CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view]; 48 | if (translation.x <= 0) { 49 | return NO; 50 | } 51 | 52 | return YES; 53 | } 54 | 55 | @end 56 | 57 | @interface XLViewControllerAnimatedTransitioningDelegate : NSObject 58 | 59 | @property (nonatomic, assign) UINavigationControllerOperation operation; 60 | 61 | @property (nonatomic, weak) UIViewController *fromViewController; 62 | 63 | @property (nonatomic, weak) UIViewController *toViewController; 64 | 65 | - (instancetype)initWithNavigationControllerOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController; 66 | 67 | @end 68 | 69 | @implementation XLViewControllerAnimatedTransitioningDelegate 70 | 71 | - (instancetype)initWithNavigationControllerOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController 72 | { 73 | self = [super init]; 74 | if (self) { 75 | self.operation = operation; 76 | self.fromViewController = fromViewController; 77 | self.toViewController = toViewController; 78 | } 79 | return self; 80 | } 81 | 82 | - (NSTimeInterval)transitionDuration:(id)transitionContext 83 | { 84 | return 0.5; 85 | } 86 | 87 | - (void)animateTransition:(id)transitionContext 88 | { 89 | UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; 90 | UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 91 | 92 | NSTimeInterval duration = [self transitionDuration:transitionContext]; 93 | // if (self.operation == UINavigationControllerOperationPush) { 94 | // [[transitionContext containerView] addSubview:fromViewController.xl_snapshot]; 95 | // fromViewController.view.hidden = YES; 96 | // 97 | // CGRect frame = [transitionContext finalFrameForViewController:toViewController]; 98 | // toViewController.view.frame = CGRectOffset(frame, CGRectGetWidth(frame), 0); 99 | // [[transitionContext containerView] addSubview:toViewController.view]; 100 | // 101 | // [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^{ 102 | // fromViewController.xl_snapshot.alpha = 0.0; 103 | // fromViewController.xl_snapshot.frame = CGRectInset(fromViewController.view.frame, 20, 20); 104 | // toViewController.view.frame = CGRectOffset(toViewController.view.frame, -CGRectGetWidth(toViewController.view.frame), 0); 105 | // } completion:^(BOOL finished) { 106 | // fromViewController.view.hidden = NO; 107 | // [fromViewController.xl_snapshot removeFromSuperview]; 108 | // [transitionContext completeTransition:YES]; 109 | // }]; 110 | // } 111 | if (self.operation == UINavigationControllerOperationPop) { 112 | [UIApplication sharedApplication].keyWindow.backgroundColor = [UIColor blackColor]; 113 | 114 | fromViewController.xl_snapshot.frame = fromViewController.navigationController.view.bounds; 115 | 116 | BOOL tabBarHidden = fromViewController.tabBarController.tabBar.hidden; 117 | 118 | fromViewController.navigationController.navigationBar.hidden = YES; 119 | fromViewController.tabBarController.tabBar.hidden = YES; 120 | 121 | toViewController.xl_snapshot.alpha = 0.5; 122 | if (toViewController.navigationController.xl_prefersOpenPopEffects) { 123 | toViewController.xl_snapshot.transform = CGAffineTransformMakeScale(0.95, 0.95); 124 | } 125 | 126 | UIView *toViewWrapperView = [[UIView alloc] initWithFrame:[transitionContext containerView].bounds]; 127 | [toViewWrapperView addSubview:toViewController.view]; 128 | toViewWrapperView.hidden = YES; 129 | 130 | [[transitionContext containerView] addSubview:toViewWrapperView]; 131 | [[transitionContext containerView] addSubview:toViewController.xl_snapshot]; 132 | [[transitionContext containerView] addSubview:fromViewController.xl_snapshot]; 133 | 134 | [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{ 135 | fromViewController.view.frame = CGRectOffset(fromViewController.view.frame, CGRectGetWidth(fromViewController.view.frame), 0); 136 | fromViewController.xl_snapshot.frame = CGRectOffset(fromViewController.xl_snapshot.frame, CGRectGetWidth(fromViewController.xl_snapshot.frame), 0); 137 | 138 | toViewController.xl_snapshot.alpha = 1.0; 139 | toViewController.xl_snapshot.transform = CGAffineTransformIdentity; 140 | } completion:^(BOOL finished) { 141 | [UIApplication sharedApplication].keyWindow.backgroundColor = [UIColor whiteColor]; 142 | toViewController.navigationController.navigationBar.hidden = NO; 143 | toViewController.tabBarController.tabBar.hidden = tabBarHidden; 144 | [fromViewController.xl_snapshot removeFromSuperview]; 145 | [toViewController.xl_snapshot removeFromSuperview]; 146 | [toViewWrapperView removeFromSuperview]; 147 | if (![transitionContext transitionWasCancelled]) { 148 | for (UIView *subView in toViewWrapperView.subviews) { 149 | [[transitionContext containerView] addSubview:subView]; 150 | } 151 | } 152 | [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; 153 | }]; 154 | } 155 | } 156 | 157 | @end 158 | 159 | @interface XLPopNavgationDelegate : NSObject 160 | 161 | @end 162 | 163 | @implementation XLPopNavgationDelegate 164 | 165 | - (id)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(XLViewControllerAnimatedTransitioningDelegate *)animationController 166 | { 167 | return animationController.fromViewController.navigationController.xl_interactivePopTransition; 168 | } 169 | 170 | - (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC 171 | { 172 | if (fromVC.navigationController.xl_interactivePopTransition) { 173 | return [[XLViewControllerAnimatedTransitioningDelegate alloc] initWithNavigationControllerOperation:operation fromViewController:fromVC toViewController:toVC]; 174 | } 175 | return nil; 176 | } 177 | 178 | @end 179 | 180 | typedef void (^XLViewControllerWillAppearInjectBlock)(UIViewController *viewController, BOOL animated); 181 | 182 | @interface UIViewController (XLPrivate) 183 | 184 | @property (nonatomic, copy) XLViewControllerWillAppearInjectBlock xl_willAppearInjectBlock; 185 | 186 | @end 187 | 188 | @implementation UIViewController (XLPrivate) 189 | 190 | + (void)load 191 | { 192 | Method originalMethod = class_getInstanceMethod(self, @selector(viewWillAppear:)); 193 | Method swizzledMethod = class_getInstanceMethod(self, @selector(xl_viewWillAppear:)); 194 | method_exchangeImplementations(originalMethod, swizzledMethod); 195 | 196 | Method disOriginalMethod = class_getInstanceMethod(self, @selector(viewWillDisappear:)); 197 | Method disSwizzledMethod = class_getInstanceMethod(self, @selector(xl_viewWillDisappear:)); 198 | method_exchangeImplementations(disOriginalMethod, disSwizzledMethod); 199 | } 200 | 201 | - (void)xl_viewWillDisappear:(BOOL)animated 202 | { 203 | [self xl_viewWillDisappear:animated]; 204 | 205 | if ([self isMovingFromParentViewController]) { 206 | self.xl_snapshot = [[UIApplication sharedApplication].keyWindow snapshotViewAfterScreenUpdates:NO]; 207 | } 208 | } 209 | 210 | - (void)xl_viewWillAppear:(BOOL)animated 211 | { 212 | [self xl_viewWillAppear:animated]; 213 | 214 | if (self.xl_willAppearInjectBlock) { 215 | self.xl_willAppearInjectBlock(self, animated); 216 | } 217 | } 218 | 219 | - (XLViewControllerWillAppearInjectBlock)xl_willAppearInjectBlock 220 | { 221 | return objc_getAssociatedObject(self, _cmd); 222 | } 223 | 224 | - (void)setXl_willAppearInjectBlock:(XLViewControllerWillAppearInjectBlock)xl_willAppearInjectBlock 225 | { 226 | objc_setAssociatedObject(self, @selector(xl_willAppearInjectBlock), xl_willAppearInjectBlock, OBJC_ASSOCIATION_COPY_NONATOMIC); 227 | } 228 | 229 | @end 230 | 231 | @implementation UINavigationController (XLPopGesture) 232 | 233 | + (void)load 234 | { 235 | Method originalMethod = class_getInstanceMethod(self, @selector(pushViewController:animated:)); 236 | Method swizzledMethod = class_getInstanceMethod(self, @selector(xl_pushViewController:animated:)); 237 | method_exchangeImplementations(originalMethod, swizzledMethod); 238 | } 239 | 240 | - (void)xl_pushViewController:(UIViewController *)viewController animated:(BOOL)animated 241 | { 242 | if (!self.delegate) { 243 | self.delegate = self.xl_popTransitionDelegate; 244 | } 245 | 246 | UIViewController *lastController = [self.viewControllers lastObject]; 247 | if (lastController.tabBarController && self.xl_prefersHiddenTabBar) { 248 | [viewController setHidesBottomBarWhenPushed:YES]; 249 | } 250 | lastController.xl_snapshot = [[UIApplication sharedApplication].keyWindow snapshotViewAfterScreenUpdates:NO]; 251 | 252 | if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.xl_popRecoginzer]) { 253 | 254 | [self.interactivePopGestureRecognizer.view addGestureRecognizer:self.xl_popRecoginzer]; 255 | 256 | self.xl_popRecoginzer.delegate = self.xl_popGestureDelegate; 257 | [self.xl_popRecoginzer addTarget:self action:@selector(handlePopRecognizer:)]; 258 | 259 | self.interactivePopGestureRecognizer.enabled = NO; 260 | 261 | } 262 | 263 | [self xl_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController]; 264 | 265 | [self xl_pushViewController:viewController animated:animated]; 266 | } 267 | 268 | - (void)xl_setupViewControllerBasedNavigationBarAppearanceIfNeeded:(UIViewController *)appearingViewController 269 | { 270 | if (!self.xl_viewControllerBasedNavigationBarAppearanceEnabled) { 271 | return; 272 | } 273 | 274 | __weak typeof(self) weakSelf = self; 275 | XLViewControllerWillAppearInjectBlock block = ^(UIViewController *viewController, BOOL animated) { 276 | __strong typeof(weakSelf) strongSelf = weakSelf; 277 | if (strongSelf) { 278 | [strongSelf setNavigationBarHidden:viewController.xl_prefersNavigationBarHidden animated:animated]; 279 | } 280 | }; 281 | 282 | appearingViewController.xl_willAppearInjectBlock = block; 283 | UIViewController *disapperingViewController = self.viewControllers.lastObject; 284 | if (disapperingViewController && !disapperingViewController.xl_willAppearInjectBlock) { 285 | disapperingViewController.xl_willAppearInjectBlock = block; 286 | } 287 | } 288 | 289 | - (void)handlePopRecognizer:(UIPanGestureRecognizer *)recognizer 290 | { 291 | CGFloat progress = [recognizer translationInView:recognizer.view].x / [UIScreen mainScreen].bounds.size.width; 292 | progress = MIN(1.0, MAX(0.0, progress)); 293 | 294 | if (recognizer.state == UIGestureRecognizerStateBegan) { 295 | self.xl_interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init]; 296 | [self popViewControllerAnimated:YES]; 297 | } else if (recognizer.state == UIGestureRecognizerStateChanged) { 298 | [self.xl_interactivePopTransition updateInteractiveTransition:progress]; 299 | } else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) { 300 | if (progress > 0.2) { 301 | [self.xl_interactivePopTransition finishInteractiveTransition]; 302 | } else { 303 | [self.xl_interactivePopTransition cancelInteractiveTransition]; 304 | } 305 | self.xl_interactivePopTransition = nil; 306 | } 307 | } 308 | 309 | - (UIPanGestureRecognizer *)xl_popRecoginzer 310 | { 311 | UIPanGestureRecognizer *panGestureRecognizer = objc_getAssociatedObject(self, _cmd); 312 | 313 | if (!panGestureRecognizer) { 314 | panGestureRecognizer = [[UIPanGestureRecognizer alloc] init]; 315 | panGestureRecognizer.maximumNumberOfTouches = 1; 316 | 317 | objc_setAssociatedObject(self, _cmd, panGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 318 | } 319 | 320 | return panGestureRecognizer; 321 | } 322 | 323 | - (XLPopNavgationDelegate *)xl_popTransitionDelegate 324 | { 325 | XLPopNavgationDelegate *delegate = objc_getAssociatedObject(self, _cmd); 326 | 327 | if (!delegate) { 328 | delegate = [[XLPopNavgationDelegate alloc] init]; 329 | 330 | objc_setAssociatedObject(self, _cmd, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 331 | } 332 | 333 | return delegate; 334 | } 335 | 336 | - (XLPopGestureDelegate *)xl_popGestureDelegate 337 | { 338 | XLPopGestureDelegate *delegate = objc_getAssociatedObject(self, _cmd); 339 | 340 | if (!delegate) { 341 | delegate = [[XLPopGestureDelegate alloc] init]; 342 | delegate.navigationController = self; 343 | 344 | objc_setAssociatedObject(self, _cmd, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 345 | } 346 | 347 | return delegate; 348 | } 349 | 350 | - (BOOL)xl_viewControllerBasedNavigationBarAppearanceEnabled 351 | { 352 | NSNumber *number = objc_getAssociatedObject(self, _cmd); 353 | if (number) { 354 | return number.boolValue; 355 | } 356 | self.xl_viewControllerBasedNavigationBarAppearanceEnabled = YES; 357 | return YES; 358 | } 359 | 360 | - (void)setXl_viewControllerBasedNavigationBarAppearanceEnabled:(BOOL)enable 361 | { 362 | SEL key = @selector(xl_viewControllerBasedNavigationBarAppearanceEnabled); 363 | objc_setAssociatedObject(self, key, @(enable), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 364 | } 365 | 366 | - (UIPercentDrivenInteractiveTransition *)xl_interactivePopTransition 367 | { 368 | return objc_getAssociatedObject(self, _cmd); 369 | } 370 | 371 | - (void)setXl_interactivePopTransition:(UIPercentDrivenInteractiveTransition *)interactivePopTransition 372 | { 373 | objc_setAssociatedObject(self, @selector(xl_interactivePopTransition), interactivePopTransition, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 374 | } 375 | 376 | - (BOOL)xl_prefersHiddenTabBar 377 | { 378 | NSNumber *number = objc_getAssociatedObject(self, _cmd); 379 | if (number) { 380 | return [number boolValue]; 381 | } 382 | return YES; 383 | } 384 | 385 | - (void)setXl_prefersHiddenTabBar:(BOOL)prefersHiddenTabBar 386 | { 387 | objc_setAssociatedObject(self, @selector(xl_prefersHiddenTabBar), @(prefersHiddenTabBar), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 388 | } 389 | 390 | - (BOOL)xl_prefersOpenPopEffects 391 | { 392 | NSNumber *number = objc_getAssociatedObject(self, _cmd); 393 | if (number) { 394 | return [number boolValue]; 395 | } 396 | return YES; 397 | } 398 | 399 | - (void)setXl_prefersOpenPopEffects:(BOOL)prefersOpenPopEffects 400 | { 401 | objc_setAssociatedObject(self, @selector(xl_prefersOpenPopEffects), @(prefersOpenPopEffects), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 402 | } 403 | 404 | @end 405 | 406 | @implementation UIViewController (XLPopGesture) 407 | 408 | - (BOOL)xl_prefersDisablePop 409 | { 410 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 411 | } 412 | 413 | - (void)setXl_prefersDisablePop:(BOOL)disable 414 | { 415 | objc_setAssociatedObject(self, @selector(xl_prefersDisablePop), @(disable), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 416 | } 417 | 418 | - (BOOL)xl_prefersNavigationBarHidden 419 | { 420 | return [objc_getAssociatedObject(self, _cmd) boolValue]; 421 | } 422 | 423 | - (void)setXl_prefersNavigationBarHidden:(BOOL)hidden 424 | { 425 | objc_setAssociatedObject(self, @selector(xl_prefersNavigationBarHidden), @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 426 | } 427 | 428 | - (UIView *)xl_snapshot 429 | { 430 | return objc_getAssociatedObject(self, _cmd); 431 | } 432 | 433 | - (void)setXl_snapshot:(UIView *)snapshot 434 | { 435 | objc_setAssociatedObject(self, @selector(xl_snapshot), snapshot, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 436 | } 437 | 438 | @end 439 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3336C4CF1E8CA1B700631AAC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3336C4CE1E8CA1B700631AAC /* main.m */; }; 11 | 3336C4D21E8CA1B700631AAC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3336C4D11E8CA1B700631AAC /* AppDelegate.m */; }; 12 | 3336C4DA1E8CA1B700631AAC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3336C4D91E8CA1B700631AAC /* Assets.xcassets */; }; 13 | 3336C4DD1E8CA1B700631AAC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3336C4DB1E8CA1B700631AAC /* LaunchScreen.storyboard */; }; 14 | 3336C4E71E8CA1C100631AAC /* UINavigationController+XLPopGesture.m in Sources */ = {isa = PBXBuildFile; fileRef = 3336C4E61E8CA1C100631AAC /* UINavigationController+XLPopGesture.m */; }; 15 | 3336C4EA1E8CA1E700631AAC /* XLOneViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3336C4E91E8CA1E700631AAC /* XLOneViewController.m */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXFileReference section */ 19 | 3336C4CA1E8CA1B700631AAC /* XLPopGestureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XLPopGestureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 20 | 3336C4CE1E8CA1B700631AAC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 21 | 3336C4D01E8CA1B700631AAC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 22 | 3336C4D11E8CA1B700631AAC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 23 | 3336C4D91E8CA1B700631AAC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 24 | 3336C4DC1E8CA1B700631AAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 25 | 3336C4DE1E8CA1B700631AAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | 3336C4E51E8CA1C100631AAC /* UINavigationController+XLPopGesture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UINavigationController+XLPopGesture.h"; sourceTree = ""; }; 27 | 3336C4E61E8CA1C100631AAC /* UINavigationController+XLPopGesture.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationController+XLPopGesture.m"; sourceTree = ""; }; 28 | 3336C4E81E8CA1E700631AAC /* XLOneViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XLOneViewController.h; sourceTree = ""; }; 29 | 3336C4E91E8CA1E700631AAC /* XLOneViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XLOneViewController.m; sourceTree = ""; }; 30 | /* End PBXFileReference section */ 31 | 32 | /* Begin PBXFrameworksBuildPhase section */ 33 | 3336C4C71E8CA1B700631AAC /* Frameworks */ = { 34 | isa = PBXFrameworksBuildPhase; 35 | buildActionMask = 2147483647; 36 | files = ( 37 | ); 38 | runOnlyForDeploymentPostprocessing = 0; 39 | }; 40 | /* End PBXFrameworksBuildPhase section */ 41 | 42 | /* Begin PBXGroup section */ 43 | 3336C4C11E8CA1B700631AAC = { 44 | isa = PBXGroup; 45 | children = ( 46 | 3336C4CC1E8CA1B700631AAC /* XLPopGestureDemo */, 47 | 3336C4CB1E8CA1B700631AAC /* Products */, 48 | ); 49 | sourceTree = ""; 50 | }; 51 | 3336C4CB1E8CA1B700631AAC /* Products */ = { 52 | isa = PBXGroup; 53 | children = ( 54 | 3336C4CA1E8CA1B700631AAC /* XLPopGestureDemo.app */, 55 | ); 56 | name = Products; 57 | sourceTree = ""; 58 | }; 59 | 3336C4CC1E8CA1B700631AAC /* XLPopGestureDemo */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 3336C4E41E8CA1C100631AAC /* XLPopGesture */, 63 | 3336C4E81E8CA1E700631AAC /* XLOneViewController.h */, 64 | 3336C4E91E8CA1E700631AAC /* XLOneViewController.m */, 65 | 3336C4D01E8CA1B700631AAC /* AppDelegate.h */, 66 | 3336C4D11E8CA1B700631AAC /* AppDelegate.m */, 67 | 3336C4D91E8CA1B700631AAC /* Assets.xcassets */, 68 | 3336C4DB1E8CA1B700631AAC /* LaunchScreen.storyboard */, 69 | 3336C4DE1E8CA1B700631AAC /* Info.plist */, 70 | 3336C4CD1E8CA1B700631AAC /* Supporting Files */, 71 | ); 72 | path = XLPopGestureDemo; 73 | sourceTree = ""; 74 | }; 75 | 3336C4CD1E8CA1B700631AAC /* Supporting Files */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | 3336C4CE1E8CA1B700631AAC /* main.m */, 79 | ); 80 | name = "Supporting Files"; 81 | sourceTree = ""; 82 | }; 83 | 3336C4E41E8CA1C100631AAC /* XLPopGesture */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 3336C4E51E8CA1C100631AAC /* UINavigationController+XLPopGesture.h */, 87 | 3336C4E61E8CA1C100631AAC /* UINavigationController+XLPopGesture.m */, 88 | ); 89 | name = XLPopGesture; 90 | path = ../../XLPopGesture; 91 | sourceTree = ""; 92 | }; 93 | /* End PBXGroup section */ 94 | 95 | /* Begin PBXNativeTarget section */ 96 | 3336C4C91E8CA1B700631AAC /* XLPopGestureDemo */ = { 97 | isa = PBXNativeTarget; 98 | buildConfigurationList = 3336C4E11E8CA1B700631AAC /* Build configuration list for PBXNativeTarget "XLPopGestureDemo" */; 99 | buildPhases = ( 100 | 3336C4C61E8CA1B700631AAC /* Sources */, 101 | 3336C4C71E8CA1B700631AAC /* Frameworks */, 102 | 3336C4C81E8CA1B700631AAC /* Resources */, 103 | ); 104 | buildRules = ( 105 | ); 106 | dependencies = ( 107 | ); 108 | name = XLPopGestureDemo; 109 | productName = XLPopGestureDemo; 110 | productReference = 3336C4CA1E8CA1B700631AAC /* XLPopGestureDemo.app */; 111 | productType = "com.apple.product-type.application"; 112 | }; 113 | /* End PBXNativeTarget section */ 114 | 115 | /* Begin PBXProject section */ 116 | 3336C4C21E8CA1B700631AAC /* Project object */ = { 117 | isa = PBXProject; 118 | attributes = { 119 | LastUpgradeCheck = 0830; 120 | ORGANIZATIONNAME = "cai cai"; 121 | TargetAttributes = { 122 | 3336C4C91E8CA1B700631AAC = { 123 | CreatedOnToolsVersion = 8.3; 124 | DevelopmentTeam = 4836KSGVBA; 125 | ProvisioningStyle = Automatic; 126 | }; 127 | }; 128 | }; 129 | buildConfigurationList = 3336C4C51E8CA1B700631AAC /* Build configuration list for PBXProject "XLPopGestureDemo" */; 130 | compatibilityVersion = "Xcode 3.2"; 131 | developmentRegion = English; 132 | hasScannedForEncodings = 0; 133 | knownRegions = ( 134 | en, 135 | Base, 136 | ); 137 | mainGroup = 3336C4C11E8CA1B700631AAC; 138 | productRefGroup = 3336C4CB1E8CA1B700631AAC /* Products */; 139 | projectDirPath = ""; 140 | projectRoot = ""; 141 | targets = ( 142 | 3336C4C91E8CA1B700631AAC /* XLPopGestureDemo */, 143 | ); 144 | }; 145 | /* End PBXProject section */ 146 | 147 | /* Begin PBXResourcesBuildPhase section */ 148 | 3336C4C81E8CA1B700631AAC /* Resources */ = { 149 | isa = PBXResourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | 3336C4DD1E8CA1B700631AAC /* LaunchScreen.storyboard in Resources */, 153 | 3336C4DA1E8CA1B700631AAC /* Assets.xcassets in Resources */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXResourcesBuildPhase section */ 158 | 159 | /* Begin PBXSourcesBuildPhase section */ 160 | 3336C4C61E8CA1B700631AAC /* Sources */ = { 161 | isa = PBXSourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | 3336C4D21E8CA1B700631AAC /* AppDelegate.m in Sources */, 165 | 3336C4EA1E8CA1E700631AAC /* XLOneViewController.m in Sources */, 166 | 3336C4CF1E8CA1B700631AAC /* main.m in Sources */, 167 | 3336C4E71E8CA1C100631AAC /* UINavigationController+XLPopGesture.m in Sources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXSourcesBuildPhase section */ 172 | 173 | /* Begin PBXVariantGroup section */ 174 | 3336C4DB1E8CA1B700631AAC /* LaunchScreen.storyboard */ = { 175 | isa = PBXVariantGroup; 176 | children = ( 177 | 3336C4DC1E8CA1B700631AAC /* Base */, 178 | ); 179 | name = LaunchScreen.storyboard; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXVariantGroup section */ 183 | 184 | /* Begin XCBuildConfiguration section */ 185 | 3336C4DF1E8CA1B700631AAC /* Debug */ = { 186 | isa = XCBuildConfiguration; 187 | buildSettings = { 188 | ALWAYS_SEARCH_USER_PATHS = NO; 189 | CLANG_ANALYZER_NONNULL = YES; 190 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 191 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 192 | CLANG_CXX_LIBRARY = "libc++"; 193 | CLANG_ENABLE_MODULES = YES; 194 | CLANG_ENABLE_OBJC_ARC = YES; 195 | CLANG_WARN_BOOL_CONVERSION = YES; 196 | CLANG_WARN_CONSTANT_CONVERSION = YES; 197 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 198 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 199 | CLANG_WARN_EMPTY_BODY = YES; 200 | CLANG_WARN_ENUM_CONVERSION = YES; 201 | CLANG_WARN_INFINITE_RECURSION = YES; 202 | CLANG_WARN_INT_CONVERSION = YES; 203 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 204 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 205 | CLANG_WARN_UNREACHABLE_CODE = YES; 206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 207 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 208 | COPY_PHASE_STRIP = NO; 209 | DEBUG_INFORMATION_FORMAT = dwarf; 210 | ENABLE_STRICT_OBJC_MSGSEND = YES; 211 | ENABLE_TESTABILITY = YES; 212 | GCC_C_LANGUAGE_STANDARD = gnu99; 213 | GCC_DYNAMIC_NO_PIC = NO; 214 | GCC_NO_COMMON_BLOCKS = YES; 215 | GCC_OPTIMIZATION_LEVEL = 0; 216 | GCC_PREPROCESSOR_DEFINITIONS = ( 217 | "DEBUG=1", 218 | "$(inherited)", 219 | ); 220 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 221 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 222 | GCC_WARN_UNDECLARED_SELECTOR = YES; 223 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 224 | GCC_WARN_UNUSED_FUNCTION = YES; 225 | GCC_WARN_UNUSED_VARIABLE = YES; 226 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 227 | MTL_ENABLE_DEBUG_INFO = YES; 228 | ONLY_ACTIVE_ARCH = YES; 229 | SDKROOT = iphoneos; 230 | }; 231 | name = Debug; 232 | }; 233 | 3336C4E01E8CA1B700631AAC /* Release */ = { 234 | isa = XCBuildConfiguration; 235 | buildSettings = { 236 | ALWAYS_SEARCH_USER_PATHS = NO; 237 | CLANG_ANALYZER_NONNULL = YES; 238 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BOOL_CONVERSION = YES; 244 | CLANG_WARN_CONSTANT_CONVERSION = YES; 245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 246 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 247 | CLANG_WARN_EMPTY_BODY = YES; 248 | CLANG_WARN_ENUM_CONVERSION = YES; 249 | CLANG_WARN_INFINITE_RECURSION = YES; 250 | CLANG_WARN_INT_CONVERSION = YES; 251 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 252 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 253 | CLANG_WARN_UNREACHABLE_CODE = YES; 254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 255 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 256 | COPY_PHASE_STRIP = NO; 257 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 258 | ENABLE_NS_ASSERTIONS = NO; 259 | ENABLE_STRICT_OBJC_MSGSEND = YES; 260 | GCC_C_LANGUAGE_STANDARD = gnu99; 261 | GCC_NO_COMMON_BLOCKS = YES; 262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 264 | GCC_WARN_UNDECLARED_SELECTOR = YES; 265 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 266 | GCC_WARN_UNUSED_FUNCTION = YES; 267 | GCC_WARN_UNUSED_VARIABLE = YES; 268 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 269 | MTL_ENABLE_DEBUG_INFO = NO; 270 | SDKROOT = iphoneos; 271 | VALIDATE_PRODUCT = YES; 272 | }; 273 | name = Release; 274 | }; 275 | 3336C4E21E8CA1B700631AAC /* Debug */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 279 | DEVELOPMENT_TEAM = 4836KSGVBA; 280 | INFOPLIST_FILE = XLPopGestureDemo/Info.plist; 281 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 282 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 283 | PRODUCT_BUNDLE_IDENTIFIER = com.xlit.XLPopGestureDemo; 284 | PRODUCT_NAME = "$(TARGET_NAME)"; 285 | }; 286 | name = Debug; 287 | }; 288 | 3336C4E31E8CA1B700631AAC /* Release */ = { 289 | isa = XCBuildConfiguration; 290 | buildSettings = { 291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 292 | DEVELOPMENT_TEAM = 4836KSGVBA; 293 | INFOPLIST_FILE = XLPopGestureDemo/Info.plist; 294 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 295 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 296 | PRODUCT_BUNDLE_IDENTIFIER = com.xlit.XLPopGestureDemo; 297 | PRODUCT_NAME = "$(TARGET_NAME)"; 298 | }; 299 | name = Release; 300 | }; 301 | /* End XCBuildConfiguration section */ 302 | 303 | /* Begin XCConfigurationList section */ 304 | 3336C4C51E8CA1B700631AAC /* Build configuration list for PBXProject "XLPopGestureDemo" */ = { 305 | isa = XCConfigurationList; 306 | buildConfigurations = ( 307 | 3336C4DF1E8CA1B700631AAC /* Debug */, 308 | 3336C4E01E8CA1B700631AAC /* Release */, 309 | ); 310 | defaultConfigurationIsVisible = 0; 311 | defaultConfigurationName = Release; 312 | }; 313 | 3336C4E11E8CA1B700631AAC /* Build configuration list for PBXNativeTarget "XLPopGestureDemo" */ = { 314 | isa = XCConfigurationList; 315 | buildConfigurations = ( 316 | 3336C4E21E8CA1B700631AAC /* Debug */, 317 | 3336C4E31E8CA1B700631AAC /* Release */, 318 | ); 319 | defaultConfigurationIsVisible = 0; 320 | }; 321 | /* End XCConfigurationList section */ 322 | }; 323 | rootObject = 3336C4C21E8CA1B700631AAC /* Project object */; 324 | } 325 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // XLPopGestureDemo 4 | // 5 | // Created by cai cai on 2017/3/30. 6 | // Copyright © 2017年 cai cai. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // XLPopGestureDemo 4 | // 5 | // Created by cai cai on 2017/3/30. 6 | // Copyright © 2017年 cai cai. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "XLOneViewController.h" 11 | #import "UINavigationController+XLPopGesture.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | // Override point for customization after application launch. 22 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 23 | 24 | UITabBarController *tabBar = [[UITabBarController alloc] init]; 25 | 26 | UINavigationController *nav1 = [[UINavigationController alloc] initWithRootViewController:[[XLOneViewController alloc] init]]; 27 | nav1.tabBarItem.title = @"首页1"; 28 | nav1.tabBarItem.image = [UIImage imageNamed:@"tabbar_selected"]; 29 | nav1.tabBarItem.selectedImage = [[UIImage imageNamed:@"tabbar"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; 30 | [tabBar addChildViewController:nav1]; 31 | 32 | UINavigationController *nav2 = [[UINavigationController alloc] initWithRootViewController:[[XLOneViewController alloc] init]]; 33 | nav2.xl_prefersHiddenTabBar = NO; 34 | nav2.tabBarItem.title = @"首页2"; 35 | nav2.tabBarItem.image = [UIImage imageNamed:@"tabbar_selected"]; 36 | nav2.tabBarItem.selectedImage = [[UIImage imageNamed:@"tabbar"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; 37 | [tabBar addChildViewController:nav2]; 38 | 39 | UINavigationController *nav3 = [[UINavigationController alloc] initWithRootViewController:[[XLOneViewController alloc] init]]; 40 | nav3.xl_prefersOpenPopEffects = NO; 41 | nav3.tabBarItem.title = @"首页3"; 42 | nav3.tabBarItem.image = [UIImage imageNamed:@"tabbar_selected"]; 43 | nav3.tabBarItem.selectedImage = [[UIImage imageNamed:@"tabbar"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; 44 | [tabBar addChildViewController:nav3]; 45 | 46 | UINavigationController *nav4 = [[UINavigationController alloc] initWithRootViewController:[[XLOneViewController alloc] init]]; 47 | nav4.xl_prefersHiddenTabBar = NO; 48 | nav4.xl_prefersOpenPopEffects = NO; 49 | nav4.tabBarItem.title = @"首页4"; 50 | nav4.tabBarItem.image = [UIImage imageNamed:@"tabbar_selected"]; 51 | nav4.tabBarItem.selectedImage = [[UIImage imageNamed:@"tabbar"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; 52 | [tabBar addChildViewController:nav4]; 53 | 54 | self.window.rootViewController = tabBar; 55 | [self.window makeKeyAndVisible]; 56 | return YES; 57 | } 58 | 59 | 60 | - (void)applicationWillResignActive:(UIApplication *)application { 61 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 62 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 63 | } 64 | 65 | 66 | - (void)applicationDidEnterBackground:(UIApplication *)application { 67 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 68 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 69 | } 70 | 71 | 72 | - (void)applicationWillEnterForeground:(UIApplication *)application { 73 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 74 | } 75 | 76 | 77 | - (void)applicationDidBecomeActive:(UIApplication *)application { 78 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 79 | } 80 | 81 | 82 | - (void)applicationWillTerminate:(UIApplication *)application { 83 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 84 | } 85 | 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "tabbar@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "tabbar@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar.imageset/tabbar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cctomato/XLPopGesture/cf00cbf9d8f0970453df959b7074b6dd5a40c121/XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar.imageset/tabbar@2x.png -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar.imageset/tabbar@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cctomato/XLPopGesture/cf00cbf9d8f0970453df959b7074b6dd5a40c121/XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar.imageset/tabbar@3x.png -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar_selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "tabbar_selected@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "tabbar_selected@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar_selected.imageset/tabbar_selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cctomato/XLPopGesture/cf00cbf9d8f0970453df959b7074b6dd5a40c121/XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar_selected.imageset/tabbar_selected@2x.png -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar_selected.imageset/tabbar_selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cctomato/XLPopGesture/cf00cbf9d8f0970453df959b7074b6dd5a40c121/XLPopGestureDemo/XLPopGestureDemo/Assets.xcassets/tabbar_selected.imageset/tabbar_selected@3x.png -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIRequiredDeviceCapabilities 26 | 27 | armv7 28 | 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/XLOneViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // XLOneViewController.h 3 | // XLPopGestureDemo 4 | // 5 | // Created by cai cai on 2017/3/30. 6 | // Copyright © 2017年 cai cai. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface XLOneViewController : UIViewController 12 | @property (nonatomic, assign) NSInteger index; 13 | @end 14 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/XLOneViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // XLOneViewController.m 3 | // XLPopGestureDemo 4 | // 5 | // Created by cai cai on 2017/3/30. 6 | // Copyright © 2017年 cai cai. All rights reserved. 7 | // 8 | 9 | #import "XLOneViewController.h" 10 | #import "UINavigationController+XLPopGesture.h" 11 | 12 | @interface XLOneViewController () 13 | 14 | @end 15 | 16 | @implementation XLOneViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view. 21 | // self.view.backgroundColor = [UIColor whiteColor]; 22 | self.view.backgroundColor = [UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1.0]; 23 | self.navigationItem.title = [NSString stringWithFormat:@"页面%ld", self.index]; 24 | 25 | [self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickTap)]]; 26 | } 27 | 28 | - (void)clickTap 29 | { 30 | XLOneViewController *vc = [[XLOneViewController alloc] init]; 31 | vc.index = self.index + 1; 32 | [self.navigationController pushViewController:vc animated:YES]; 33 | } 34 | 35 | /* 36 | #pragma mark - Navigation 37 | 38 | // In a storyboard-based application, you will often want to do a little preparation before navigation 39 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 40 | // Get the new view controller using [segue destinationViewController]. 41 | // Pass the selected object to the new view controller. 42 | } 43 | */ 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /XLPopGestureDemo/XLPopGestureDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // XLPopGestureDemo 4 | // 5 | // Created by cai cai on 2017/3/30. 6 | // Copyright © 2017年 cai cai. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | --------------------------------------------------------------------------------