First of all, after iOS7, the system has its own default sideslip function, when the user slides on the left side of the interface, there will be a sideslip function.

However, if we look at the back button of the slave navigation controller, we find that the system has a slide-back function that does not work, and some functions are not satisfactory. The advantage of system customization is when there are other conflicting gestures in the interface

(such as the swipe or left-swipe gesture of a controller interface itself), the system sliding method is edge gesture, and the area of action may be different from other gestures, which can help resolve these conflicts.

So there are the following custom methods.

1. Full-screen gesture swiping

– (void)viewDidLoad {

      [super viewDidLoad];

// Get the target object of the system’s built-in sliding gesture

      id target = self.interactivePopGestureRecognizer.delegate;

// Create a full-screen swipe gesture by calling target’s action method

        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];

// Set up gesture proxy to block gesture trigger

     pan.delegate = self;

// Add a full-screen swipe gesture to the navigation controller’s view

[self.view addGestureRecognizer:pan];

// Disable system sliding gestures

self.interactivePopGestureRecognizer.enabled = NO;

}

// When to invoke: Each time the gesture is triggered, the proxy is asked if it is triggered.

// Action: block gesture trigger

– (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

{

// Note: only non-root controllers have slide-back functionality, not root controllers.

// Check whether the navigation controller has only one child controller. If there is only one child controller, it must be the root controller

if (self.childViewControllers.count == 1) {

// Indicates that the user is in the root controller interface, so there is no need to trigger the sliding gesture,

return NO;

}

return YES;

}

This method enables full-screen gesture swiping, but conflicts may occur when other gestures are present in the current view.


2. Some views do not return by right swipe

@property (nonatomic,strong) UIViewController *currentShowVC;

-(void)viewWillAppear:(BOOL)animated

{

self.navigationController.navigationBarHidden=YES;

[_firstVC hidenLabel];

// Set the proxy

self.navigationController.interactivePopGestureRecognizer.delegate =(id)self;

// Enable the system’s built-in swipe gesture

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

/ / Determine the number of views on the stack that should be returned without gesture, usually 1, I set it to 2 because there is a blank following view

if (self.navigationController.viewControllers.count == 2){

// Empty the current navigation controller

self.currentShowVC = Nil;

}else{

self.currentShowVC = self;

}

}

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer

{

if (gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {

// The current navigation controller is the root view controller

//the most important

return (self.currentShowVC == self.navigationController.topViewController);

}

return YES;

}


Some screens do not return gestures:

1. In the current interface

-(void)viewDidAppear:(BOOL)animated

{

self.navigationController.interactivePopGestureRecognizer.enabled = NO;

}

2. The next interface needs to use the return gesture

-(void)viewWillAppear:(BOOL)animated

{

// Set the proxy

self.navigationController.interactivePopGestureRecognizer.delegate =(id)self;

// Enable the system’s built-in swipe gesture

self.navigationController.interactivePopGestureRecognizer.enabled = YES;

}

3. Customize the return animation

.h files

#import

@interface AnimatedNavigationController : UINavigationController

@end

.m files

#import “AnimatedNavigationController.h”

@interface AnimatedNavigationController ()

@end

#import “ViewController.h”

@interface AnimatedNavigationController ()

{

// Screen capture

UIImageView *_screenshotImgView;

// Screenshot of the black translucent mask above

UIView *_coverView;

// Save all screenshots

NSMutableArray *_screenshotImgs;

}

@end

@implementation AnimatedNavigationController

– (void)viewDidLoad

{

[super viewDidLoad];

// 1, create Pan gesture recognizer and bind listener method

UIPanGestureRecognizer *panGestureRec = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRec:)];

// Add Pan gesture recognizer to navigation controller view

[self.view addGestureRecognizer:panGestureRec];

// 2. Create an ImageView for the screenshot

_screenshotImgView = [[UIImageView alloc] init];

// App frame is a frame with the status bar height removed

_screenshotImgView.frame = [UIScreen mainScreen].applicationFrame;

/ / (0 to 20; 320, 460);

// 3. Create the black translucent mask above the screenshot

_coverView = [[UIView alloc] init];

// The frame of the mask is the frame of the screenshot

_coverView.frame = _screenshotImgView.frame;

// The mask is black

_coverView.backgroundColor = [UIColor blackColor];

// 4. Store all the screenshot array initialization

_screenshotImgs = [NSMutableArray array];

}

// Override the push method to capture the image before the push

– (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated

{

// Screenshots are only needed if the navigation controller has child controllers

if (self.viewControllers.count >= 1) {

// Call custom methods, using context screenshots

[self screenShot];

}

// After the screenshot is taken, the push method of the parent class is called

[super pushViewController:viewController animated:YES];

}

// Use context screenshots and crop the template code with the specified area

– (void)screenShot

{

// The view to be captured is the view of the root controller of the window (must not contain the status bar, controllers in ios7 contain the status bar by default)

UIViewController *beyondVC = (UIViewController *)self.view.window.rootViewController;

// Total size of background image

CGSize size = beyondVC.view.frame.size;

// Open the context and use the parameter to extract the original image (YES 0.0 high quality)

UIGraphicsBeginImageContextWithOptions (size, YES, 0.0);

// The range of rectangles to crop

CGRect rect = CGRectMake(0, -20.8, sie.width, sie.height + 20);

RenderInContext: drawViewHierarchyInRect: afterScreenUpdates: drawViewHierarchyInRect: afterScreenUpdates

[beyondVC.view drawViewHierarchyInRect:rect  afterScreenUpdates:NO];

// From the context, get UIImage

UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();

// Add the captured image to the image array

[_screenshotImgs addObject:snapshot];

// Remember to end the context (remove the current bitmap based graphics context at the top of the stack)

UIGraphicsEndImageContext();

}

// The method of listening for gestures, as long as there are gestures will be executed

– (void)panGestureRec:(UIPanGestureRecognizer *)panGestureRec

{

// If the current controller is already the root controller, do not need to do any switching animation, directly return

if(self.topViewController == self.viewControllers[0]) return;

// Determine the stages of the pan gesture

switch (panGestureRec.state) {

case UIGestureRecognizerStateBegan:

// Start the drag phase

[self dragBegin];

break;

case UIGestureRecognizerStateEnded:

// End the drag phase

[self dragEnd];

break;

default:

// In the drag phase

[self dragging:panGestureRec];

break;

}

}

#pragma Mark starts dragging to add images and masks

– (void)dragBegin

{

// Add the screenshot imageView and cover cover to the window every time you start the Pan gesture

[self.view.window insertSubview:_screenshotImgView atIndex:0];

[self.view.window insertSubview:_coverView aboveSubview:_screenshotImgView];

// Also, let imgView display the last (most recent) screenshot in the screenshot array

_screenshotImgView.image = [_screenshotImgs lastObject];

}

// The default initial scale of the screenshot to be scaled

# define kDefaultScale 0.6

// The default initial transparency of the mask to be transparent (all black)

# define kDefaultAlpha 1.0

// When the drag distance takes up 3/4 of the total width and height of the screen, the imageView is fully displayed and the masking disappears completely

# define kTargetTranslateScale 0.75

#pragma Mark is dragging, the essence of animation effects, to scale and change transparency

– (void)dragging:(UIPanGestureRecognizer *)pan

{

// Get the displacement of the finger drag

CGFloat offsetX = [pan translationInView:self.view].x;

// Only drag to the right, not the left

if (offsetX < 0) offsetX = 0;

// Move the entire navigation view

self.view.transform = CGAffineTransformMakeTranslation(offsetX, 0);

// Calculate the ratio of the current finger drag to the total width and height of the screen. When this ratio reaches 3/4, let the ImageView fully display, masking completely disappear

double currentTranslateScaleX = offsetX/self.view.frame.size.width;

// Let the imageView scale, the default scale +(current balance scale/target balance scale)*(1- default scale)

double scale = kDefaultScale + (currentTranslateScaleX/kTargetTranslateScale) * (1 – kDefaultScale);

// It has reached the original size, it is ok, do not need to put more

if (scale > 1) scale = 1;

_screenshotImgView.transform = CGAffineTransformMakeScale(scale, scale);

// Let the opacity of the mask change until it is reduced to 0 to make the mask fully transparent, default ratio -(current balance ratio/target balance ratio)* default ratio

double alpha = kDefaultAlpha – (currentTranslateScaleX/kTargetTranslateScale) * kDefaultAlpha;

_coverView.alpha = alpha;

}

#pragma mark ends a drag, determines the distance at the end of the drag and processes it accordingly, and removes the picture and mask from the parent control

– (void)dragEnd

{

// Remove the distance to be moved

CGFloat translateX = self.view.transform.tx;

// Get the width

CGFloat width = self.view.frame.size.width;

If (translateX <= width * 0.5) {

// If your finger is less than halfway across the screen, move it to the left (bounce back)

[UIView animateWithDuration: 0.3 animations: ^ {

// Important ~~ to bounce a view back to its position, simply empty the transform

self.view.transform = CGAffineTransformIdentity;

// Restore the imageView size to its default scale

_screenshotImgView.transform = CGAffineTransformMakeScale(kDefaultScale, kDefaultScale);

// Restore the opacity of masking to alpha 1.0 by default

_coverView.alpha = kDefaultAlpha;

} completion:^(BOOL finished) {

// Important, remember to remove two views each time after animation, and add them the next time you start dragging

[_screenshotImgView removeFromSuperview];

[_coverView removeFromSuperview];

}];

} else {

// If your finger moves more than halfway across the screen, move to the right

[UIView animateWithDuration: 0.3 animations: ^ {

// Move the view completely to the far right of the screen. After that, remember to empty the view’s transform

self.view.transform = CGAffineTransformMakeTranslation(width, 0);

// Set the imageView scale to 1

_screenshotImgView.transform = CGAffineTransformMakeScale(1, 1);

// Change the cover alpha to 0 and become fully transparent

_coverView.alpha = 0;

} completion:^(BOOL finished) {

// Important ~~ move the view completely to the far right of the screen. After that, remember to empty the view’s transform, otherwise the next time you start drag, the view’s transform is not zero

self.view.transform = CGAffineTransformIdentity;

// Remove two views and add them back the next time you start dragging

[_screenshotImgView removeFromSuperview];

[_coverView removeFromSuperview];

// Perform the normal Pop operation: remove the top of the stack controller so that the actual previous controller becomes the top of the stack controller for the navigation controller

[self popViewControllerAnimated:NO];

// Important ~ remember that at this point, you can remove the last useless screenshot in the screenshot array

[_screenshotImgs removeLastObject];

}];

}

}

@end










The demo address: https://github.com/hyf12138/NavigationDemo.git