Skip to content

Instantly share code, notes, and snippets.

@hikui
Forked from siqin/SwipeNavigationController.h
Last active January 1, 2016 14:39
Show Gist options
  • Save hikui/8159256 to your computer and use it in GitHub Desktop.
Save hikui/8159256 to your computer and use it in GitHub Desktop.
/*
#####################################################################
# File : MHGSwipeNavigationController.h
# Project : StockBar
# Created : 13-12-30
# DevTeam : Thomas
# Author : 缪 和光
# Notes :
#####################################################################
### Change Logs ###################################################
#####################################################################
---------------------------------------------------------------------
# Date :
# Author:
# Notes :
#
#####################################################################
*/
#import <UIKit/UIKit.h>
@interface EMSwipeNavigationController : UINavigationController<UINavigationControllerDelegate>
/**
* You can disable this gesture where you do not want the "swipe to pop" feature.
* But you should NEVER set the delegate of this gesture recognizer to your own class,
* since I have to use this delegate to ignore the vertical pan.
*/
@property (nonatomic, strong, readonly) UIPanGestureRecognizer *mhg_popGestureRecognizer;
/**
* You should ALWAYS use this property as the delegate instead of self.navigationController.delegate.
*/
@property (nonatomic, unsafe_unretained) id<UINavigationControllerDelegate> secondLevelDelegate;
@end
/*
#####################################################################
# File : MHGSwipeNavigationController.m
# Project : StockBar
# Created : 13-12-30
# DevTeam : Thomas
# Author : 缪 和光
# Notes :
#####################################################################
### Change Logs ###################################################
#####################################################################
---------------------------------------------------------------------
# Date :
# Author:
# Notes :
#
#####################################################################
*/
#if !__has_feature(objc_arc)
#error "This file should be compiled with ARC!"
#endif
#import "EMSwipeNavigationController.h"
#define KEY_WINDOW [[UIApplication sharedApplication] keyWindow]
#define TOP_VIEW (KEY_WINDOW.rootViewController.view)
@interface EMSwipeNavigationController () <UIGestureRecognizerDelegate>
@property (nonatomic, strong) NSMutableArray *snapshotStack;
@property (nonatomic, strong) UIImageView *snapshotImageView;
@property (nonatomic, assign) CGFloat lastTouchX;
@property (nonatomic, assign) dispatch_queue_t imageCompressQueue;
@end
@implementation EMSwipeNavigationController
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self commonInit];
}
return self;
}
- (id)initWithRootViewController:(UIViewController *)rootViewController {
self = [super initWithRootViewController:rootViewController];
if (self) {
[self commonInit];
}
return self;
}
- (id)init {
self = [super init];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
}
self.delegate = self;
_snapshotImageView = [[UIImageView alloc]init];
_imageCompressQueue = dispatch_queue_create("EMSwipeNavigationControllerImageCompressQueue", NULL);
}
- (void)dealloc
{
if (_imageCompressQueue) {
dispatch_release(_imageCompressQueue);
_imageCompressQueue = NULL;
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
[self.view addGestureRecognizer:panGestureRecognizer];
_mhg_popGestureRecognizer = panGestureRecognizer;
_mhg_popGestureRecognizer.delegate = self;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// [self addShadowForView];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.snapshotImageView removeFromSuperview];
}
#pragma mark - capture last view's snapshot
- (UIImage *)takeSnapshot
{
UIGraphicsBeginImageContextWithOptions(TOP_VIEW.bounds.size, YES, 0.0);
[TOP_VIEW.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshot;
}
#pragma mark - override push
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (self.viewControllers.count != 0 || self.view.superview != nil) {
// if self.view is in the view hierarchy or it has more than 1 viewController, then the snapshot should be taken.
if (!self.snapshotStack) {
self.snapshotStack = [[NSMutableArray alloc] initWithCapacity:5];
}
UIImage *snapshot = [self takeSnapshot];
self.snapshotImageView.image = snapshot;
dispatch_async(self.imageCompressQueue, ^{
NSData *snapshotData = UIImagePNGRepresentation(snapshot);
if (snapshotData) [self.snapshotStack addObject:snapshotData];
});
}
[super pushViewController:viewController animated:animated];
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
if (self.viewControllers.count != 0 || self.view.superview != nil) {
dispatch_async(self.imageCompressQueue, ^{
[self.snapshotStack removeLastObject];
NSData *snapshotData = [self.snapshotStack lastObject];
if (snapshotData) {
UIImage *snapshot = [UIImage imageWithData:snapshotData];
dispatch_async(dispatch_get_main_queue(), ^{
self.snapshotImageView.image = snapshot;
});
}
});
}
return [super popViewControllerAnimated:animated];
}
- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated {
NSArray *popedStack = [super popToViewController:viewController animated:animated];
dispatch_async(self.imageCompressQueue, ^{
//出图片栈
[self.snapshotStack removeObjectsInRange:NSMakeRange(self.snapshotStack.count - popedStack.count, popedStack.count)];
NSData *snapshotData = [self.snapshotStack lastObject];
if (snapshotData) {
UIImage *snapshot = [UIImage imageWithData:snapshotData];
dispatch_async(dispatch_get_main_queue(), ^{
self.snapshotImageView.image = snapshot;
});
}
});
return popedStack;
}
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated {
dispatch_sync(self.imageCompressQueue, ^{
[self.snapshotStack removeAllObjects];
});
return [super popToRootViewControllerAnimated:animated];
}
#pragma mark - handlePanGesture
- (void)handlePanGesture:(UIPanGestureRecognizer *)panGestureRecognizer
{
if (self.viewControllers.count <= 1) return ;
CGPoint point = [panGestureRecognizer locationInView:KEY_WINDOW];
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan) {
[self ajustSnapshotFrame];
[self addShadowForView];
self.snapshotImageView.hidden = NO;
[TOP_VIEW.superview insertSubview:self.snapshotImageView belowSubview:TOP_VIEW];
self.lastTouchX = point.x;
} else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
CGRect frame = TOP_VIEW.frame;
CGFloat newX = (point.x - self.lastTouchX) + frame.origin.x;
if (newX < 0) {
return;
}
frame.origin.x = newX;
TOP_VIEW.frame = frame;
self.lastTouchX = point.x;
[self offsetImageViewForX:newX];
} else {
[self judgeToPushOrPop];
}
}
- (void)ajustSnapshotFrame
{
CGRect imageViewFrame = TOP_VIEW.bounds;
imageViewFrame.origin.x = -TOP_VIEW.bounds.size.width / 3;
self.snapshotImageView.frame = imageViewFrame;
}
#pragma mark - judgeToPushOrPop
- (void)judgeToPushOrPop
{
__block CGRect frame = TOP_VIEW.frame;
if (frame.origin.x > (frame.size.width / 3)) {
[UIView animateWithDuration:0.3 animations:^{
frame.origin.x = frame.size.width;
TOP_VIEW.frame = frame;
[self offsetImageViewForX:frame.origin.x];
} completion:^(BOOL finished) {
[self popViewControllerAnimated:NO];
self.snapshotImageView.hidden = YES;
[self removeShadowForView];
frame.origin.x = 0;
TOP_VIEW.frame = frame;
}];
} else {
[UIView animateWithDuration:0.3 animations:^{
frame.origin.x = 0;
TOP_VIEW.frame = frame;
[self offsetImageViewForX:frame.origin.x];
} completion:^(BOOL finished) {
self.snapshotImageView.hidden = YES;
[self removeShadowForView];
}];
}
}
- (void)offsetImageViewForX:(CGFloat)x {
CGFloat imageViewX = (x - TOP_VIEW.bounds.size.width) /3 ;
CGRect imageViewFrame = self.snapshotImageView.frame;
imageViewFrame.origin.x = imageViewX;
self.snapshotImageView.frame = imageViewFrame;
}
- (void)addShadowForView {
UIView *shadowedView = TOP_VIEW;
UIBezierPath* newShadowPath = [UIBezierPath bezierPathWithRect:shadowedView.bounds];
shadowedView.layer.masksToBounds = NO;
shadowedView.layer.shadowRadius = 10;
shadowedView.layer.shadowOpacity = 1;
shadowedView.layer.shadowColor = [[UIColor blackColor] CGColor];
shadowedView.layer.shadowOffset = CGSizeZero;
shadowedView.layer.shadowPath = [newShadowPath CGPath];
}
- (void)removeShadowForView {
UIView *shadowedView = TOP_VIEW;
shadowedView.layer.masksToBounds = YES;
shadowedView.layer.shadowRadius = 0;
shadowedView.layer.shadowPath = NULL;
}
#pragma mark - gesture recognizer delegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.mhg_popGestureRecognizer) {
CGPoint velocity = [self.mhg_popGestureRecognizer velocityInView:self.view];
return ABS(velocity.x) > ABS(velocity.y); // Horizontal panning
}else{
return YES;
}
}
#pragma mark UINavigationControllerDelegate
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
self.mhg_popGestureRecognizer.enabled = NO;
if ([self.secondLevelDelegate respondsToSelector:@selector(navigationController:willShowViewController:animated:)]) {
[self.secondLevelDelegate navigationController:navigationController willShowViewController:viewController animated:animated];
}
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
self.mhg_popGestureRecognizer.enabled = YES;
if ([self.secondLevelDelegate respondsToSelector:@selector(navigationController:didShowViewController:animated:)]) {
[self.secondLevelDelegate navigationController:navigationController didShowViewController:viewController animated:animated];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment