// // SlideMenuController.m // btWeighBase // // Created by ChipSea on 16/1/28. // Copyright © 2016年 Chipsea. All rights reserved. // #import "SlideMenuController.h" typedef enum { SlideActionOpen, SlideActionClose }SlideAction; struct PanInfo { SlideAction action; BOOL shouldBounce; CGFloat velocity; }; @implementation SlideMenuOption -(instancetype)init { self = [super init]; if (self) { _leftViewWitdth = 250; _leftBezelWidth = 16.0; _contentViewScale = 1.0; _contentViewOpacity = 0.5; _shadowOpacity = 0.0; _shadowRadius = 0.0; _shadowOffset = CGSizeMake(0, 0); _panFromBezel = YES; _animationDuration = .4; _rightViewWidth = 250; _rightBezelWidth = 16.0; _rightPanFromBezel = YES; _hideStatusBar = YES; _pointOfNoReturnWidth = 44; _simultaneousGestureRecognizers = YES; _opacityViewBackgroundColor = [UIColor blackColor]; } return self; } @end /** * Instead of the struct static variables * Instead of LeftPanState and RightPanState */ static CGRect LPSFrameAtStartOfPan = {0,0,0,0}; static CGPoint LPSStartPointOfPan = {0,0}; static BOOL LPSWasOpenAtStartOfPan = NO; static BOOL LPSWasHiddenAtStartOfPan = NO; static CGRect RPSFrameAtStartOfPan = {0,0,0,0}; static UIGestureRecognizerState LPSLastState = UIGestureRecognizerStateEnded; static CGPoint RPSStartPointOfPan = {0,0}; static BOOL RPSWasOpenAtStartOfPan = NO; static BOOL RPSWasHiddenAtStartOfPan = NO; static UIGestureRecognizerState RPSLastState = UIGestureRecognizerStateEnded; @interface SlideMenuController () { SlideMenuOption *options; } @end @implementation SlideMenuController -(instancetype)init { self = [super init]; if (self) { _mainContainerView = [UIView new]; _leftContainerView = [UIView new]; _rightContainerView = [UIView new]; options = [[SlideMenuOption alloc] init]; } return self; } -(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { return [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; } -(instancetype)initWithCoder:(NSCoder *)aDecoder { return [super initWithCoder:aDecoder]; } -(instancetype)initWithMainViewController:(UIViewController *)tMainController leftMenuViewController:(UIViewController *)tLeftMenuController { self = [self init]; if (self) { _mainViewController = tMainController; _leftViewController = tLeftMenuController; [self initView]; } return self; } -(instancetype)initWithMainViewController:(UIViewController *)tMainController rightMenuViewController:(UIViewController *)tRightMenuController { self = [self init]; if (self) { _mainViewController = tMainController; _rightViewController = tRightMenuController; [self initView]; } return self; } -(instancetype)initWithMainViewController:(UIViewController *)tMainController leftMenuViewController:(UIViewController *)tLeftMenuController rightMenuViewController:(UIViewController *)tRightMenuController { self = [self init]; if (self) { _mainViewController = tMainController; _leftViewController = tLeftMenuController; _rightViewController = tRightMenuController; [self initView]; } return self; } -(void)awakeFromNib { [super awakeFromNib]; [self initView]; } -(void)initView { _mainContainerView = [[UIView alloc] initWithFrame:self.view.bounds]; _mainContainerView.backgroundColor = [UIColor clearColor]; _mainContainerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; [self.view insertSubview:_mainContainerView atIndex:0]; CGRect opacityFrame = self.view.bounds; CGFloat opacityOffset = 0; opacityFrame.origin.y = opacityFrame.origin.y + opacityOffset; opacityFrame.size.height = opacityFrame.size.height - opacityOffset; _opacityView = [[UIView alloc] initWithFrame:opacityFrame]; _opacityView.backgroundColor = options.opacityViewBackgroundColor; _opacityView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _opacityView.layer.opacity = 0.0; [self.view insertSubview:_opacityView atIndex:1]; CGRect leftFrame = self.view.bounds; leftFrame.size.width = options.leftViewWitdth; leftFrame.origin.x = [self leftMinOrigin]; CGFloat leftOffset = 0; leftFrame.origin.y = leftFrame.origin.y + leftOffset; leftFrame.size.height = leftFrame.size.height - leftOffset; _leftContainerView = [[UIView alloc] initWithFrame:leftFrame]; _leftContainerView.backgroundColor = [UIColor clearColor]; _leftContainerView.autoresizingMask = UIViewAutoresizingFlexibleHeight; [self.view insertSubview:_leftContainerView atIndex:2]; CGRect rightFrame = self.view.bounds; rightFrame.size.width = options.rightViewWidth; rightFrame.origin.x = [self rightMinOrigin]; CGFloat rightOffset = 0; rightFrame.origin.y = rightFrame.origin.y + rightOffset; rightFrame.size.height = rightFrame.size.height - rightOffset; _rightContainerView = [[UIView alloc] initWithFrame:rightFrame]; _rightContainerView.backgroundColor = [UIColor clearColor]; _rightContainerView.autoresizingMask = UIViewAutoresizingFlexibleHeight ; [self.view insertSubview:_rightContainerView atIndex:3]; [self addLeftGestures]; [self addRightGestures]; } -(SlideMenuOption *)getOption { return options; } -(void)setOption:(SlideMenuOption *)option { options = option; } //-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration { // [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; -(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; _mainContainerView.transform = CGAffineTransformMakeScale(1.0, 1.0); _leftContainerView.hidden = YES; _rightContainerView.hidden = YES; __block typeof(self) weakSelf = self; [coordinator animateAlongsideTransition:^(id _Nonnull context) { } completion:^(id _Nonnull context) { [weakSelf closeLeftNonAnimation]; [weakSelf closeRightNonAnimation]; weakSelf.leftContainerView.hidden = NO; weakSelf.rightContainerView.hidden = NO; if (weakSelf.leftPanGesture != nil && weakSelf.leftTapGesture != nil) { [weakSelf removeLeftGestures]; [weakSelf addLeftGestures]; } if (weakSelf.rightPanGesture != nil && weakSelf.rightTapGesture != nil) { [weakSelf removeRightGestures]; [weakSelf addRightGestures]; } }]; } -(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; [self closeLeftNonAnimation]; [self closeRightNonAnimation]; _leftContainerView.hidden = NO; _rightContainerView.hidden = NO; [self removeLeftGestures]; [self removeRightGestures]; [self addLeftGestures]; [self addRightGestures]; } - (void)viewDidLoad { [super viewDidLoad]; self.edgesForExtendedLayout = UIRectEdgeNone; } -(UIInterfaceOrientationMask)supportedInterfaceOrientations { if (_mainViewController != nil) { return [_mainViewController supportedInterfaceOrientations]; } return UIInterfaceOrientationMaskAll; } -(void)viewWillLayoutSubviews { [self setUpViewController:_mainContainerView targetViewController:_mainViewController]; [self setUpViewController:_leftContainerView targetViewController:_leftViewController]; [self setUpViewController:_rightContainerView targetViewController:_rightViewController]; } -(void)openLeft { if (_leftViewController == nil) { return; } if (_delegate != nil && [_delegate respondsToSelector:@selector(leftWillOpen)]) { [_delegate leftWillOpen]; } [self setOpenWindowLevel]; if (_leftViewController != nil) { [_leftViewController beginAppearanceTransition:[self isLeftHidden] animated:YES]; } [self openLeftWithVelocity:0.0]; [self track:TrackActionLeftTapOpen]; } -(void)openRight { if (_rightViewController == nil) { return; } if (_delegate != nil && [_delegate respondsToSelector:@selector(rightWillOpen)]) { [_delegate rightWillOpen]; } [self setOpenWindowLevel]; if (_rightViewController != nil) { [_rightViewController beginAppearanceTransition:[self isRightHidden] animated:YES]; } [self openRightWithVelocity:0.0]; [self track:TrackActionRightTapOpen]; } -(void)closeLeft { if (_leftViewController == nil) { return; } if (_delegate != nil && [_delegate respondsToSelector:@selector(leftWillClose)]) { [_delegate leftWillClose]; } [_leftViewController beginAppearanceTransition:[self isLeftHidden] animated:YES]; [self closeLeftWithVelocity:0.0]; [self setCloseWindowLebel]; } -(void)closeRight { if (_rightViewController == nil) { return; } if (_delegate != nil && [_delegate respondsToSelector:@selector(rightWillClose)]) { [_delegate rightWillClose]; } [_rightViewController beginAppearanceTransition:[self isRightHidden] animated:YES]; [self closeRightWithVelocity:0.0]; [self setCloseWindowLebel]; } -(void)addLeftGestures { if (_leftViewController != nil) { // if (self.leftPanGesture == nil) { // self.leftPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleLeftPanGesture:)]; // self.leftPanGesture.delegate = self; // [self.view addGestureRecognizer:self.leftPanGesture]; // } if (self.leftTapGesture == nil) { self.leftTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleLeft)]; self.leftTapGesture.delegate = self; [self.view addGestureRecognizer:self.leftTapGesture]; } } } -(void)addRightGestures { if (_rightViewController != nil) { if (self.rightPanGesture == nil) { self.rightPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleRightPanGesture:)]; self.rightPanGesture.delegate = self; [self.view addGestureRecognizer:self.rightPanGesture]; } if (self.rightTapGesture == nil) { self.rightTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(toggleRight)]; self.rightTapGesture.delegate = self; [self.view addGestureRecognizer:self.rightTapGesture]; } } } -(void)removeLeftGestures { if (self.leftPanGesture != nil) { [self.view removeGestureRecognizer:self.leftPanGesture]; self.leftPanGesture = nil; } if (self.leftTapGesture != nil) { [self.view removeGestureRecognizer:self.leftTapGesture]; self.leftTapGesture = nil; } } -(void)removeRightGestures { if (self.rightPanGesture != nil) { [self.view removeGestureRecognizer:self.rightPanGesture]; self.rightPanGesture = nil; } if (self.rightTapGesture != nil) { [self.view removeGestureRecognizer:self.rightTapGesture]; self.rightTapGesture = nil; } } -(BOOL)isTagetViewController { // Function to determine the target ViewController // Please to override it if necessary return YES; } -(void)track:(TrackAction)action { // function is for tracking // Please to override it if necessary } -(void)handleLeftPanGesture:(UIPanGestureRecognizer *)panGesture { if (![self isTagetViewController]) { return; } if (![self isRightHidden]) { return; } switch (panGesture.state) { case UIGestureRecognizerStateBegan: { if (LPSLastState != UIGestureRecognizerStateEnded) { return; } if (_delegate != nil) { if ([self isLeftHidden]) { if ([_delegate respondsToSelector:@selector(leftWillOpen)]) { [_delegate leftWillOpen]; } } else { if ([_delegate respondsToSelector:@selector(leftWillClose)]) { [_delegate leftWillClose]; } } } LPSFrameAtStartOfPan = _leftContainerView.frame; LPSStartPointOfPan = [panGesture locationInView:self.view]; LPSWasOpenAtStartOfPan = [self isLeftOpen]; LPSWasHiddenAtStartOfPan = [self isLeftHidden]; if (_leftViewController != nil) { [_leftViewController beginAppearanceTransition:LPSWasHiddenAtStartOfPan animated:YES]; } [self addShadowToView:_leftContainerView]; [self setOpenWindowLevel]; break; } case UIGestureRecognizerStateChanged: { if (LPSLastState != UIGestureRecognizerStateBegan && LPSLastState != UIGestureRecognizerStateChanged) { return; } CGPoint translation = [panGesture translationInView:panGesture.view]; _leftContainerView.frame = [self applyLeftTranslation:translation toFrame:LPSFrameAtStartOfPan]; [self applyLeftOpacity]; [self applyLeftContentViewScale]; break; } case UIGestureRecognizerStateEnded: { if (LPSLastState != UIGestureRecognizerStateChanged) { return; } CGPoint velocity = [panGesture velocityInView:panGesture.view]; struct PanInfo panInfo = [self panLeftResultInfoForVelocity:velocity]; if (panInfo.action == SlideActionOpen) { if (LPSWasHiddenAtStartOfPan && _leftViewController != nil) { [_leftViewController beginAppearanceTransition:YES animated:YES]; } [self openLeftWithVelocity:panInfo.velocity]; [self track:TrackActionLeftFlickOpen]; } else { if (LPSWasHiddenAtStartOfPan && _leftViewController != nil) { [_leftViewController beginAppearanceTransition:NO animated:YES]; } [self closeLeftWithVelocity:panInfo.velocity]; [self setCloseWindowLebel]; [self track:TrackActionLeftTapClose]; } break; } default: break; } LPSLastState = panGesture.state; } -(void)handleRightPanGesture:(UIPanGestureRecognizer *)panGesture { if (![self isTagetViewController]) { return; } if (![self isLeftHidden]) { return; } switch (panGesture.state) { case UIGestureRecognizerStateBegan: { if (RPSLastState != UIGestureRecognizerStateEnded) { return; } if (_delegate != nil) { if ([self isLeftHidden]) { if ([_delegate respondsToSelector:@selector(rightWillOpen)]) { [_delegate rightWillOpen]; } } else { if ([_delegate respondsToSelector:@selector(rightWillClose)]) { [_delegate rightWillClose]; } } } RPSFrameAtStartOfPan = _rightContainerView.frame; RPSStartPointOfPan = [panGesture locationInView:self.view]; RPSWasOpenAtStartOfPan = [self isRightOpen]; RPSWasHiddenAtStartOfPan = [self isRightHidden]; if (_rightViewController != nil) { [_rightViewController beginAppearanceTransition:RPSWasHiddenAtStartOfPan animated:YES]; } [self addShadowToView:_rightContainerView]; [self setOpenWindowLevel]; break; } case UIGestureRecognizerStateChanged: { if (RPSLastState != UIGestureRecognizerStateBegan && RPSLastState != UIGestureRecognizerStateChanged) { return; } CGPoint translation = [panGesture translationInView:panGesture.view]; _rightContainerView.frame = [self applyRightTranslation:translation toFrame:RPSFrameAtStartOfPan]; [self applyRightOpacity]; [self applyRightContentViewScale]; NSLog(@"handleRightPanGesture --> Changed frame:%@", NSStringFromCGRect(_rightContainerView.frame)); break; } case UIGestureRecognizerStateEnded: { if (RPSLastState != UIGestureRecognizerStateChanged) { return; } CGPoint velocity = [panGesture velocityInView:panGesture.view]; struct PanInfo panInfo = [self panRightResultInfoForVelocity:velocity]; if (panInfo.action == SlideActionOpen) { if (RPSWasHiddenAtStartOfPan && _rightViewController != nil) { [_rightViewController beginAppearanceTransition:YES animated:YES]; } [self openRightWithVelocity:panInfo.velocity]; } else { if (RPSWasHiddenAtStartOfPan && _rightViewController != nil) { [_rightViewController beginAppearanceTransition:NO animated:YES]; } [self closeRightWithVelocity:panInfo.velocity]; [self setCloseWindowLebel]; } NSLog(@"handleRightPanGesture --> Ended frame:%@", NSStringFromCGRect(_rightContainerView.frame)); break; } default: break; } RPSLastState = panGesture.state; } -(void)openLeftWithVelocity:(CGFloat)velocity { CGFloat xOrigin = _leftContainerView.frame.origin.x; CGFloat finalXOrigin = 0.0; CGRect frame = _leftContainerView.frame; frame.origin.x = finalXOrigin; NSTimeInterval duration = options.animationDuration; if (velocity != 0.0) { duration = fabs(xOrigin - finalXOrigin) / velocity; duration = fmax(0.1, fmin(1.0, duration)); } [self addShadowToView:_leftContainerView]; __block typeof(self) weakSelf = self; [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ weakSelf.leftContainerView.frame = frame; weakSelf.opacityView.layer.opacity = options.contentViewOpacity; weakSelf.mainContainerView.transform = CGAffineTransformMakeScale(options.contentViewScale, options.contentViewScale); } completion:^(BOOL finished) { [weakSelf disableContentInteraction]; [weakSelf.leftViewController endAppearanceTransition]; if (weakSelf.delegate != nil && [weakSelf.delegate respondsToSelector:@selector(leftDidOpen)]) { [weakSelf.delegate leftDidOpen]; } }]; } -(void)openRightWithVelocity:(CGFloat)velocity { CGFloat xOrigin = _rightContainerView.frame.origin.x; CGFloat finalXOrigin = CGRectGetWidth(self.view.bounds) - _rightContainerView.frame.size.width; CGRect frame = _rightContainerView.frame; frame.origin.x = finalXOrigin; NSTimeInterval duration = options.animationDuration; if (velocity != 0.0) { duration = fabs(xOrigin - CGRectGetWidth(self.view.bounds)) / velocity; duration = fmax(0.1, fmin(1.0, duration)); } [self addShadowToView:_rightContainerView]; __block typeof(self) weakSelf = self; [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ weakSelf.rightContainerView.frame = frame; weakSelf.opacityView.layer.opacity = options.contentViewOpacity; weakSelf.mainContainerView.transform = CGAffineTransformMakeScale(options.contentViewScale, options.contentViewScale); } completion:^(BOOL finished) { [weakSelf disableContentInteraction]; [weakSelf.rightViewController endAppearanceTransition]; if (weakSelf.delegate != nil && [weakSelf.delegate respondsToSelector:@selector(rightDidOpen)]) { [weakSelf.delegate rightDidOpen]; } }]; } -(void)closeLeftWithVelocity:(CGFloat) velocity { CGFloat xOrigin = _leftContainerView.frame.origin.x; CGFloat finalXOrigin = [self leftMinOrigin]; CGRect frame = _leftContainerView.frame; frame.origin.x = finalXOrigin; NSTimeInterval duration = options.animationDuration; if (velocity != 0.0) { duration = fabs(xOrigin - finalXOrigin) / velocity; duration = fmax(0.1, fmin(1.0, duration)); } __block typeof(self) weakSelf = self; [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ weakSelf.leftContainerView.frame = frame; weakSelf.opacityView.layer.opacity = 0.0; weakSelf.mainContainerView.transform = CGAffineTransformMakeScale(1.0, 1.0); } completion:^(BOOL finished) { [weakSelf removeShadow:weakSelf.leftContainerView]; [weakSelf enableContentInteraction]; [weakSelf.leftViewController endAppearanceTransition]; if (weakSelf.delegate != nil && [weakSelf.delegate respondsToSelector:@selector(leftDidClose)]) { [weakSelf.delegate leftDidClose]; } }]; } -(void)closeRightWithVelocity:(CGFloat) velocity { CGFloat xOrigin = _rightContainerView.frame.origin.x; CGFloat finalXOrigin = CGRectGetWidth(self.view.bounds); CGRect frame = _rightContainerView.frame; frame.origin.x = finalXOrigin; NSTimeInterval duration = options.animationDuration; if (velocity != 0.0) { duration = fabs(xOrigin - CGRectGetWidth(self.view.bounds)) / velocity; duration = fmax(0.1, fmin(1.0, duration)); } __block typeof(self) weakSelf = self; [UIView animateWithDuration:duration delay:0.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ weakSelf.rightContainerView.frame = frame; weakSelf.opacityView.layer.opacity = 0.0; weakSelf.mainContainerView.transform = CGAffineTransformMakeScale(1.0, 1.0); } completion:^(BOOL finished) { [weakSelf removeShadow:weakSelf.rightContainerView]; [weakSelf enableContentInteraction]; [weakSelf.rightViewController endAppearanceTransition]; if (weakSelf.delegate != nil && [weakSelf.delegate respondsToSelector:@selector(rightDidClose)]) { [weakSelf.delegate rightDidClose]; } }]; } -(void)toggleLeft { if ([self isLeftOpen]) { [self closeLeft]; [self setCloseWindowLebel]; [self track:TrackActionLeftTapClose]; } else { [self openLeft]; } } -(BOOL)isLeftOpen { return _leftViewController != nil && _leftContainerView.frame.origin.x == 0.0; } -(BOOL)isLeftHidden { return _leftContainerView.frame.origin.x <= [self leftMinOrigin]; } -(void)toggleRight { if ([self isRightOpen]) { [self closeRight]; [self setCloseWindowLebel]; [self track:TrackActionRightTapClose]; } else { [self openRight]; } } -(BOOL)isRightOpen { return _rightViewController != nil && _rightContainerView.frame.origin.x == CGRectGetWidth(self.view.bounds) - _rightContainerView.frame.size.width; } -(BOOL)isRightHidden { return _rightContainerView.frame.origin.x >= CGRectGetWidth(self.view.bounds); } -(void)setPanFromBezel:(BOOL)panFromBezel { options.panFromBezel = panFromBezel; } -(void)changeMainViewController:(UIViewController *)newMainController close:(BOOL)close { [self removeViewController:_mainViewController]; _mainViewController = newMainController; [self setUpViewController:_mainContainerView targetViewController:_mainViewController]; if (close) { [self closeLeft]; [self closeRight]; } } -(void)changeLeftViewWidth:(CGFloat)width { options.leftViewWitdth = width; CGRect leftFrame = self.view.bounds; leftFrame.size.width = width; leftFrame.origin.x = [self leftMinOrigin]; CGFloat leftOffset = 0; leftFrame.origin.y = leftFrame.origin.y + leftOffset; leftFrame.size.height = leftFrame.size.height - leftOffset; _leftContainerView.frame = leftFrame; } -(void)changeRightViewWidth:(CGFloat)width { options.rightBezelWidth = width; CGRect rightFrame = self.view.bounds; rightFrame.size.width = width; rightFrame.origin.x = [self rightMinOrigin]; CGFloat rightOffset = 0; rightFrame.origin.y = rightFrame.origin.y + rightOffset; rightFrame.size.height = rightFrame.size.height - rightOffset; _leftContainerView.frame = rightFrame; } -(void)changeLeftViewController:(UIViewController *)newLeftController close:(BOOL) close { [self removeViewController:_leftViewController]; _leftViewController = newLeftController; [self setUpViewController:_leftContainerView targetViewController:_leftViewController]; if (close) { [self closeLeft]; } } -(void)changeRightViewController:(UIViewController *)newRightController close:(BOOL) close { [self removeViewController:_rightViewController]; _rightViewController = newRightController; [self setUpViewController:_rightContainerView targetViewController:_rightViewController]; if (close) { [self closeRight]; } } -(CGFloat)leftMinOrigin { return -options.leftViewWitdth; } -(CGFloat)rightMinOrigin { return CGRectGetWidth(self.view.bounds); } -(struct PanInfo)panLeftResultInfoForVelocity:(CGPoint)velocity { CGFloat thresholdVelocity = 1000.0; CGFloat pointOfNoReturn = floor([self leftMinOrigin]) + options.pointOfNoReturnWidth; CGFloat leftOrigin = _leftContainerView.frame.origin.x; struct PanInfo panInfo = {SlideActionClose, NO, 0.0}; panInfo.action = leftOrigin <= pointOfNoReturn ? SlideActionClose : SlideActionOpen; if (velocity.x >= thresholdVelocity) { panInfo.action = SlideActionOpen; panInfo.velocity = velocity.x; } else if (velocity.x <= (-1.0 * thresholdVelocity)) { panInfo.action = SlideActionClose; panInfo.velocity = velocity.x; } return panInfo; } -(struct PanInfo)panRightResultInfoForVelocity:(CGPoint)velocity { CGFloat thresholdVelocity = -1000; CGFloat pointOfNoReturn = floor(CGRectGetWidth(self.view.bounds)) - options.pointOfNoReturnWidth; CGFloat rightOrigin = _rightContainerView.frame.origin.x; struct PanInfo panInfo = {SlideActionClose, NO, 0.0}; panInfo.action = rightOrigin >= pointOfNoReturn ? SlideActionClose : SlideActionOpen; if (velocity.x <= thresholdVelocity) { panInfo.action = SlideActionOpen; panInfo.velocity = velocity.x; } else if (velocity.x >= (-1.0 * thresholdVelocity)) { panInfo.action = SlideActionClose; panInfo.velocity = velocity.x; } return panInfo; } -(CGRect)applyLeftTranslation:(CGPoint)translation toFrame:(CGRect)frame { CGFloat newOrigin = frame.origin.x; newOrigin += translation.x; CGFloat minOrigin = [self leftMinOrigin]; CGFloat maxOrigin = 0.0; CGRect newFrame = frame; if (newOrigin < minOrigin) { newOrigin = minOrigin; } else if(newOrigin > maxOrigin) { newOrigin = maxOrigin; } newFrame.origin.x = newOrigin; return newFrame; } -(CGRect)applyRightTranslation:(CGPoint)translation toFrame:(CGRect)frame { CGFloat newOrigin = frame.origin.x; newOrigin += translation.x; CGFloat minOrigin = [self rightMinOrigin]; CGFloat maxOrigin = [self rightMinOrigin] - _rightContainerView.frame.size.width; CGRect newFrame = frame; if (newOrigin > minOrigin) { newOrigin = minOrigin; } else if (newOrigin < maxOrigin) { newOrigin = maxOrigin; } newFrame.origin.x = newOrigin; return newFrame; } -(CGFloat)getOpenedLeftRatio { CGFloat width = _leftContainerView.frame.size.width; CGFloat currentPosition = _leftContainerView.frame.origin.x - [self leftMinOrigin]; return currentPosition / width; } -(CGFloat)getOpenedRightRatio { CGFloat width = _rightContainerView.frame.size.width; CGFloat currentPosition = _rightContainerView.frame.origin.x; return -(currentPosition - CGRectGetWidth(self.view.bounds))/width; } -(void)applyLeftOpacity { CGFloat openedLeftRatio = [self getOpenedLeftRatio]; CGFloat opacity = options.contentViewOpacity * openedLeftRatio; _opacityView.layer.opacity = opacity; } -(void)applyRightOpacity { CGFloat openedRightRatio = [self getOpenedRightRatio]; CGFloat opacity = options.contentViewOpacity * openedRightRatio; _opacityView.layer.opacity = opacity; } -(void)applyLeftContentViewScale { CGFloat openedLeftRadio = [self getOpenedLeftRatio]; CGFloat scale = 1.0 - ((1.0 - options.contentViewScale) * openedLeftRadio); _mainContainerView.transform = CGAffineTransformMakeScale(scale, scale); } -(void)applyRightContentViewScale { CGFloat openedRightRatio = [self getOpenedRightRatio]; CGFloat scale = 1.0 - ((1.0 - options.contentViewScale) * openedRightRatio); _mainContainerView.transform = CGAffineTransformMakeScale(scale, scale); } -(void)addShadowToView:(UIView *)targetContainerView { targetContainerView.layer.masksToBounds = NO; targetContainerView.layer.shadowOffset = options.shadowOffset; targetContainerView.layer.shadowOpacity = options.shadowOpacity; targetContainerView.layer.shadowRadius = options.shadowRadius; targetContainerView.layer.shadowPath = [UIBezierPath bezierPathWithRect:targetContainerView.bounds].CGPath; } -(void)removeShadow:(UIView *)targetContainerView { targetContainerView.layer.masksToBounds = YES; _mainContainerView.layer.opacity = 1.0; } -(void)removeContentOpacity { _opacityView.layer.opacity = 0.0; } -(void)addContentOpacity { _opacityView.layer.opacity = options.contentViewOpacity; } -(void)disableContentInteraction { _mainContainerView.userInteractionEnabled = NO; } -(void)enableContentInteraction { _mainContainerView.userInteractionEnabled = YES; } -(void)setOpenWindowLevel { if (options.hideStatusBar) { dispatch_async(dispatch_get_main_queue(), ^{ if ([UIApplication sharedApplication].keyWindow != nil) { [UIApplication sharedApplication].keyWindow.windowLevel = UIWindowLevelStatusBar + 1; } }); } } -(void)setCloseWindowLebel { if (options.hideStatusBar) { dispatch_async(dispatch_get_main_queue(), ^{ if ([UIApplication sharedApplication].keyWindow != nil) { [UIApplication sharedApplication].keyWindow.windowLevel = UIWindowLevelNormal; } }); } } -(void)setUpViewController:(UIView *)targetView targetViewController:(UIViewController *)targetViewController { if (targetViewController != nil) { [self addChildViewController:targetViewController]; targetViewController.view.frame = targetView.bounds; [targetView addSubview:targetViewController.view]; [targetViewController didMoveToParentViewController:self]; } } -(void)removeViewController:(UIViewController *)viewController { if (viewController != nil) { [viewController willMoveToParentViewController:nil]; [viewController.view removeFromSuperview]; [viewController removeFromParentViewController]; } } -(void)closeLeftNonAnimation { [self setCloseWindowLebel]; CGFloat finalXOrigin = [self leftMinOrigin]; CGRect frame = _leftContainerView.frame; frame.origin.x = finalXOrigin; _leftContainerView.frame = frame; _opacityView.layer.opacity = 0.0; _mainContainerView.transform = CGAffineTransformMakeScale(1.0, 1.0); [self removeShadow:_leftContainerView]; [self enableContentInteraction]; } -(void)closeRightNonAnimation { [self setCloseWindowLebel]; CGFloat finalXOrigin = CGRectGetWidth(self.view.bounds); CGRect frame = _rightContainerView.frame; frame.origin.x = finalXOrigin; _rightContainerView.frame = frame; _opacityView.layer.opacity = 0.0; _mainContainerView.transform = CGAffineTransformMakeScale(1.0, 1.0); [self removeShadow:_rightContainerView]; [self enableContentInteraction]; } #pragma mark - UIGestureRecognizerDelegate -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { CGPoint point = [touch locationInView:self.view]; if (gestureRecognizer == _leftPanGesture) { return [self slideLeftForGestureRecognizer:gestureRecognizer inPoint:point]; } else if (gestureRecognizer == _rightPanGesture) { return [self slideRightViewForGestureRecognizer:gestureRecognizer withTouchPoint:point]; } else if (gestureRecognizer == _leftTapGesture) { return [self isLeftOpen] && ![self isPointContainedWithinLeftRect:point]; } else if (gestureRecognizer == _rightTapGesture) { return [self isRightOpen] && ![self isPointContainedWithinRightRect:point]; } return YES; } -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return options.simultaneousGestureRecognizers; } -(BOOL)slideLeftForGestureRecognizer:(UIGestureRecognizer *)gesture inPoint:(CGPoint)point { return [self isLeftOpen] || (options.panFromBezel && [self isLeftPointContainedWithinBezelRect:point]); } -(BOOL)isLeftPointContainedWithinBezelRect:(CGPoint)point { CGRect leftBezelRect = CGRectZero; CGRect tempRect = CGRectZero; CGFloat bezelWidth = CGRectGetWidth(self.view.bounds) - options.leftBezelWidth; CGRectDivide(self.view.bounds, &leftBezelRect, &tempRect, bezelWidth, CGRectMinXEdge); return CGRectContainsPoint(leftBezelRect, point); } -(BOOL)isPointContainedWithinLeftRect:(CGPoint)point { return CGRectContainsPoint(_leftContainerView.frame, point);; } -(BOOL)slideRightViewForGestureRecognizer:(UIGestureRecognizer *)gesture withTouchPoint:(CGPoint)point { return [self isRightOpen] || (options.rightPanFromBezel && [self isRightPointContainedWithinBezelRect:point]); } -(BOOL)isRightPointContainedWithinBezelRect:(CGPoint)point { CGRect rightBezelRect = CGRectZero; CGRect tempRect = CGRectZero; CGFloat bezelWidth = CGRectGetWidth(self.view.bounds) - options.rightBezelWidth; CGRectDivide(self.view.bounds, &tempRect, &rightBezelRect, bezelWidth, CGRectMinXEdge); return CGRectContainsPoint(rightBezelRect, point); } -(BOOL)isPointContainedWithinRightRect:(CGPoint)point { return CGRectContainsPoint(_rightContainerView.frame, point); } @end @interface UIViewController(SlideMenuVC) @property (retain, nonatomic, readonly, getter=slideMenuController) SlideMenuController *slideMenuController; @end @implementation UIViewController(SlideMenuVC) -(SlideMenuController *)slideMenuController { UIViewController *controller = self; while (controller != nil) { if ([controller isKindOfClass:[SlideMenuController class]]) { return (SlideMenuController *)controller; } controller = [controller parentViewController]; } return nil; } -(void)addLeftBarButtonWithImage:(UIImage *)buttonImage { UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithImage:buttonImage style:UIBarButtonItemStyleBordered target:self action:@selector(toggleLeft)]; self.navigationItem.leftBarButtonItem = leftButton; } -(void)addRightBarButtonWithImage:(UIImage *)buttonImage { UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithImage:buttonImage style:UIBarButtonItemStyleBordered target:self action:@selector(toggleRight)]; self.navigationItem.rightBarButtonItem = leftButton; } -(void)toggleLeft { if (self.slideMenuController != nil) { [self.slideMenuController toggleLeft]; } } -(void)toggleRight { if (self.slideMenuController != nil) { [self.slideMenuController toggleRight]; } } -(void)openLeft { if (self.slideMenuController != nil) { [self.slideMenuController openLeft]; } } -(void)openRight { if (self.slideMenuController != nil) { [self.slideMenuController openRight]; } } -(void)closeLeft { if (self.slideMenuController != nil) { [self.slideMenuController closeLeft]; } } -(void)closeRight { if (self.slideMenuController != nil) { [self.slideMenuController closeRight]; } } -(void)addPriorityToMenuGesture:(UIScrollView *) targetScrollView { if (self.slideMenuController != nil) { NSArray *recognizers = self.slideMenuController.view.gestureRecognizers; for (UIGestureRecognizer *recognizer in recognizers) { if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) { [targetScrollView.panGestureRecognizer requireGestureRecognizerToFail:recognizer]; } } } } @end