diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cc8bc54..87f02467 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ #MMDrawerController Changelog +##[0.3.0](https://github.com/mutualmobile/MMDrawerController/issues?milestone=6&page=1&state=closed) (Monday, July 22nd, 2013) +###New +* Added a block to determine if a gesture should be recognized, giving the implementer a chance to define where a gesture should be recognized within their views. Please consult the [README](https://github.com/mutualmobile/mmdrawercontroller#custom-gesture-recognizer-support) for additional details. ([#25](https://github.com/mutualmobile/MMDrawerController/pull/25)). (Kevin Harwood) +###Fixed +* **FIXED** an issue ([#56](https://github.com/mutualmobile/MMDrawerController/pull/56)) where the bezel gesture would be detected even if there was no drawer controller on that side. (Kevin Harwood) +* **FIXED** an issue ([#50](https://github.com/mutualmobile/MMDrawerController/pull/50)) where a subclass could get stuck in an infinite loop in the `init` method. (Tuan Cao) + +##[0.2.1](https://github.com/mutualmobile/MMDrawerController/issues?milestone=7&state=closed) (Friday, June 21st, 2013) +###Fixed +* **FIXED** an issue([#42](https://github.com/mutualmobile/MMDrawerController/issues/42)) where the gesture completion block was not being called if the gesture action closed the drawer completely. (Kevin Harwood) + +##[0.2.0](https://github.com/mutualmobile/MMDrawerController/issues?milestone=5&state=closed) (Tuesday, June 4th, 2013) +###New +* Added support for using the panning velocity to complete the animation. It now looks *much* better ([#18](https://github.com/mutualmobile/MMDrawerController/issues/18)). (Kevin Harwood) +* Added a new callback block to get notified when a gesture animation has completed ([#20](https://github.com/mutualmobile/MMDrawerController/issues/20)). (Kevin Harwood) + +###Fixed +* **FIXED** an issue([#23](https://github.com/mutualmobile/MMDrawerController/issues/23)) where the drawer could bounce, even if a drawer was open. (Kevin Harwood) +* **FIXED** an issue([#38](https://github.com/mutualmobile/MMDrawerController/issues/38)) the designator initializer for `UIViewController` was not properly setting default values. (poteryaysya) +* **FIXED** an issue([#24](https://github.com/mutualmobile/MMDrawerController/issues/24)) where some documentation was incorrect. (Kevin Harwood) + ##[0.1.0](https://github.com/mutualmobile/MMDrawerController/issues?milestone=3&page=1&state=closed) (Wednesday, May 15th, 2013) ###New * `MMDrawerController` now properly supports full view controller containment. The drawer view controllers will properly receive their view appearance methods at the correct time now, including every time they are about to become visible. Please note that `mm_drawerWillApear` has now been deprecated. More notes below. (Kevin Harwood, Lars Anderson) diff --git a/MMDrawerController.podspec b/MMDrawerController.podspec index b34f92de..e651c77c 100644 --- a/MMDrawerController.podspec +++ b/MMDrawerController.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = "MMDrawerController" - s.version = "0.1.0" + s.version = "0.3.0" s.summary = "A lightweight, easy-to-use side drawer navigation controller." s.homepage = "https://github.com/mutualmobile/MMDrawerController" s.license = { :type => 'MIT', :file => 'LICENSE' } s.author = { "Kevin Harwood" => "kevin.harwood@mutualmobile.com" } - s.source = { :git => "https://github.com/mutualmobile/MMDrawerController.git", :tag => "0.1.0" } + s.source = { :git => "https://github.com/mutualmobile/MMDrawerController.git", :tag => "0.3.0" } s.platform = :ios, '5.0' s.requires_arc = true s.screenshots = [ "http://mutualmobile.github.io/MMDrawerController/ExampleImages/example1.png", diff --git a/MMDrawerController/MMDrawerController.h b/MMDrawerController/MMDrawerController.h index 7e125ea9..5d06d7b2 100644 --- a/MMDrawerController/MMDrawerController.h +++ b/MMDrawerController/MMDrawerController.h @@ -58,9 +58,11 @@ typedef NS_OPTIONS(NSInteger, MMOpenDrawerGestureMode) { MMOpenDrawerGestureModePanningNavigationBar = 1 << 1, MMOpenDrawerGestureModePanningCenterView = 1 << 2, MMOpenDrawerGestureModeBezelPanningCenterView = 1 << 3, - MMOpenDrawerGestureModeAll = MMOpenDrawerGestureModePanningNavigationBar | - MMOpenDrawerGestureModePanningCenterView | - MMOpenDrawerGestureModeBezelPanningCenterView, + MMOpenDrawerGestureModeCustom = 1 << 4, + MMOpenDrawerGestureModeAll = MMOpenDrawerGestureModePanningNavigationBar | + MMOpenDrawerGestureModePanningCenterView | + MMOpenDrawerGestureModeBezelPanningCenterView | + MMOpenDrawerGestureModeCustom, }; typedef NS_OPTIONS(NSInteger, MMCloseDrawerGestureMode) { @@ -71,12 +73,14 @@ typedef NS_OPTIONS(NSInteger, MMCloseDrawerGestureMode) { MMCloseDrawerGestureModeTapNavigationBar = 1 << 4, MMCloseDrawerGestureModeTapCenterView = 1 << 5, MMCloseDrawerGestureModePanningDrawerView = 1 << 6, + MMCloseDrawerGestureModeCustom = 1 << 7, MMCloseDrawerGestureModeAll = MMCloseDrawerGestureModePanningNavigationBar | MMCloseDrawerGestureModePanningCenterView | MMCloseDrawerGestureModeBezelPanningCenterView | MMCloseDrawerGestureModeTapNavigationBar | MMCloseDrawerGestureModeTapCenterView | - MMCloseDrawerGestureModePanningDrawerView, + MMCloseDrawerGestureModePanningDrawerView | + MMCloseDrawerGestureModeCustom, }; typedef NS_ENUM(NSInteger, MMDrawerOpenCenterInteractionMode) { @@ -200,8 +204,6 @@ typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * dr /** Creates and initializes an `MMDrawerController` object with the specified center view controller, left drawer view controller, and right drawer view controller. - This is the designated initializer. - @param centerViewController The center view controller. This argument must not be `nil`. @param leftDrawerViewController The left drawer view controller. @param rightDrawerViewController The right drawer controller. @@ -279,7 +281,7 @@ typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * dr @param completion The block called when the animation is finsihed. */ --(void)setCenterViewController:(UIViewController *)centerViewController withCloseAnimation:(BOOL)closeAnimated completion:(void(^)(BOOL))completion; +-(void)setCenterViewController:(UIViewController *)centerViewController withCloseAnimation:(BOOL)closeAnimated completion:(void(^)(BOOL finished))completion; /** Sets the new `centerViewController`. @@ -291,7 +293,7 @@ typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * dr @param completion The block called when the animation is finsihed. */ --(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)fullCloseAnimated completion:(void(^)(BOOL))completion; +-(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)fullCloseAnimated completion:(void(^)(BOOL finished))completion; ///--------------------------------------- /// @name Animating the Width of a Drawer @@ -363,4 +365,32 @@ typedef void (^MMDrawerControllerDrawerVisualStateBlock)(MMDrawerController * dr */ -(void)setDrawerVisualStateBlock:(void(^)(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible))drawerVisualStateBlock; +///--------------------------------------- +/// @name Gesture Completion Handling +///--------------------------------------- + +/** + Sets a callback to be called when a gesture has been completed. + + This block is called when a gesture action has been completed. You can query the `openSide` of the `drawerController` to determine what the new state of the drawer is. + + @param gestureCompletionBlock A block object to be called that allows the implementer be notified when a gesture action has been completed. + */ +-(void)setGestureCompletionBlock:(void(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture))gestureCompletionBlock; + +///--------------------------------------- +/// @name Custom Gesture Handler +///--------------------------------------- + +/** + Sets a callback to be called to determine if a UIGestureRecognizer should recieve the given UITouch. + + This block provides a way to allow a gesture to be recognized with custom logic. For example, you may have a certain part of your view that should accept a pan gesture recognizer to open the drawer, but not another a part. If you return YES, the gesture is recognized and the appropriate action is taken. This provides similar support to how Facebook allows you to pan on the background view of the main table view, but not the content itself. You can inspect the `openSide` property of the `drawerController` to determine the current state of the drawer, and apply the appropriate logic within your block. + + Note that either `openDrawerGestureModeMask` must contain `MMOpenDrawerGestureModeCustom`, or `closeDrawerGestureModeMask` must contain `MMCloseDrawerGestureModeCustom` for this block to be consulted. + + @param gestureShouldRecognizeTouchBlock A block object to be called to determine if the given `touch` should be recognized by the given gesture. + */ +-(void)setGestureShouldRecognizeTouchBlock:(BOOL(^)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch))gestureShouldRecognizeTouchBlock; + @end diff --git a/MMDrawerController/MMDrawerController.m b/MMDrawerController/MMDrawerController.m index 07b41cc8..629469eb 100644 --- a/MMDrawerController/MMDrawerController.m +++ b/MMDrawerController/MMDrawerController.m @@ -49,6 +49,9 @@ /** The percent of the possible overshoot width to use as the actual overshoot percentage. */ CGFloat const MMDrawerOvershootPercentage = 0.1f; +typedef BOOL (^MMDrawerGestureShouldRecognizeTouchBlock)(MMDrawerController * drawerController, UIGestureRecognizer * gesture, UITouch * touch); +typedef void (^MMDrawerGestureCompletionBlock)(MMDrawerController * drawerController, UIGestureRecognizer * gesture); + static CAKeyframeAnimation * bounceKeyFrameAnimationForDistanceOnView(CGFloat distance, UIView * view) { CGFloat factors[32] = {0, 32, 60, 83, 100, 114, 124, 128, 128, 124, 114, 100, 83, 60, 32, 0, 24, 42, 54, 62, 64, 62, 54, 42, 24, 0, 18, 28, 32, 28, 18, 0}; @@ -120,35 +123,41 @@ @interface MMDrawerController () { @property (nonatomic, assign) CGRect startingPanRect; @property (nonatomic, copy) MMDrawerControllerDrawerVisualStateBlock drawerVisualState; +@property (nonatomic, copy) MMDrawerGestureShouldRecognizeTouchBlock gestureShouldRecognizeTouch; +@property (nonatomic, copy) MMDrawerGestureCompletionBlock gestureCompletion; +@property (nonatomic, assign, getter = isAnimatingDrawer) BOOL animatingDrawer; @end @implementation MMDrawerController +#pragma mark - Init + +- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { + self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; + if (self) { + [self setMaximumLeftDrawerWidth:MMDrawerDefaultWidth]; + [self setMaximumRightDrawerWidth:MMDrawerDefaultWidth]; + + [self setAnimationVelocity:MMDrawerDefaultAnimationVelocity]; + + [self setShowsShadow:YES]; + [self setShouldStretchDrawer:YES]; + + [self setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeNone]; + [self setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeNone]; + [self setCenterHiddenInteractionMode:MMDrawerOpenCenterInteractionModeNavigationBarOnly]; + } + return self; +} + -(id)initWithCenterViewController:(UIViewController *)centerViewController leftDrawerViewController:(UIViewController *)leftDrawerViewController rightDrawerViewController:(UIViewController *)rightDrawerViewController{ NSParameterAssert(centerViewController); - self = [self init]; + self = [super init]; if(self){ - [self setCenterViewController:centerViewController]; [self setLeftDrawerViewController:leftDrawerViewController]; [self setRightDrawerViewController:rightDrawerViewController]; - - [self setMaximumLeftDrawerWidth:MMDrawerDefaultWidth]; - [self setMaximumRightDrawerWidth:MMDrawerDefaultWidth]; - - [self setAnimationVelocity:MMDrawerDefaultAnimationVelocity]; - - [self setShowsShadow:YES]; - [self setShouldStretchDrawer:YES]; - - [self setOpenDrawerGestureModeMask:MMOpenDrawerGestureModeNone]; - [self setCloseDrawerGestureModeMask:MMCloseDrawerGestureModeNone]; - [self setCenterHiddenInteractionMode:MMDrawerOpenCenterInteractionModeNavigationBarOnly]; - - [self.view setBackgroundColor:[UIColor blackColor]]; - - [self setupGestureRecognizers]; } return self; } @@ -162,7 +171,7 @@ -(id)initWithCenterViewController:(UIViewController *)centerViewController right } #pragma mark - Open/Close methods --(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL))completion{ +-(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ NSParameterAssert(drawerSide!=MMDrawerSideNone); if(self.openSide == MMDrawerSideNone){ [self openDrawerSide:drawerSide animated:animated completion:completion]; @@ -180,89 +189,45 @@ -(void)toggleDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated complet } } --(void)closeDrawerAnimated:(BOOL)animated completion:(void (^)(BOOL))completion{ +-(void)closeDrawerAnimated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ [self closeDrawerAnimated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; } --(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion{ - CGRect newFrame = self.view.bounds; - - CGFloat distance = ABS(CGRectGetMinX(self.centerContainerView.frame)); - NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); - - BOOL leftDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) > 0; - BOOL rightDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) < 0; - - MMDrawerSide visibleSide = MMDrawerSideNone; - CGFloat percentVisble = 0.0; - - if(leftDrawerVisible){ - CGFloat visibleDrawerPoints = CGRectGetMinX(self.centerContainerView.frame); - percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumLeftDrawerWidth); - visibleSide = MMDrawerSideLeft; - } - else if(rightDrawerVisible){ - CGFloat visibleDrawerPoints = CGRectGetWidth(self.centerContainerView.frame)-CGRectGetMaxX(self.centerContainerView.frame); - percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumRightDrawerWidth); - visibleSide = MMDrawerSideRight; - } - - UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide]; - - [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisble]; - - [sideDrawerViewController beginAppearanceTransition:NO animated:animated]; - - [UIView - animateWithDuration:(animated?duration:0.0) - delay:0.0 - options:options - animations:^{ - [self.centerContainerView setFrame:newFrame]; - [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:0.0]; - } - completion:^(BOOL finished) { - [sideDrawerViewController endAppearanceTransition]; - [self setOpenSide:MMDrawerSideNone]; - [self resetDrawerVisualStateForDrawerSide:visibleSide]; - - if(completion){ - completion(finished); - } - }]; -} - --(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL))completion{ - NSParameterAssert(drawerSide != MMDrawerSideNone); - - [self openDrawerSide:drawerSide animated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; -} - --(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL))completion{ - NSParameterAssert(drawerSide != MMDrawerSideNone); - - UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; - CGRect visibleRect = CGRectIntersection(self.view.bounds,sideDrawerViewController.view.frame); - BOOL drawerFullyCovered = (CGRectContainsRect(self.centerContainerView.frame, visibleRect) || - CGRectIsNull(visibleRect)); - if(drawerFullyCovered){ - [self prepareToPresentDrawer:drawerSide animated:animated]; +-(void)closeDrawerAnimated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion{ + if (self.isAnimatingDrawer) { + if(completion){ + completion(NO); + } } - - if(sideDrawerViewController){ - CGRect newFrame; - CGRect oldFrame = self.centerContainerView.frame; - if(drawerSide == MMDrawerSideLeft){ - newFrame = self.centerContainerView.frame; - newFrame.origin.x = self.maximumLeftDrawerWidth; + else { + self.animatingDrawer = animated; + CGRect newFrame = self.view.bounds; + + CGFloat distance = ABS(CGRectGetMinX(self.centerContainerView.frame)); + NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + + BOOL leftDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) > 0; + BOOL rightDrawerVisible = CGRectGetMinX(self.centerContainerView.frame) < 0; + + MMDrawerSide visibleSide = MMDrawerSideNone; + CGFloat percentVisble = 0.0; + + if(leftDrawerVisible){ + CGFloat visibleDrawerPoints = CGRectGetMinX(self.centerContainerView.frame); + percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumLeftDrawerWidth); + visibleSide = MMDrawerSideLeft; } - else { - newFrame = self.centerContainerView.frame; - newFrame.origin.x = 0-self.maximumRightDrawerWidth; + else if(rightDrawerVisible){ + CGFloat visibleDrawerPoints = CGRectGetWidth(self.centerContainerView.frame)-CGRectGetMaxX(self.centerContainerView.frame); + percentVisble = MAX(0.0,visibleDrawerPoints/self.maximumRightDrawerWidth); + visibleSide = MMDrawerSideRight; } - CGFloat distance = ABS(CGRectGetMinX(oldFrame)-newFrame.origin.x); - NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:visibleSide]; + + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:percentVisble]; + + [sideDrawerViewController beginAppearanceTransition:NO animated:animated]; [UIView animateWithDuration:(animated?duration:0.0) @@ -270,17 +235,13 @@ -(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity: options:options animations:^{ [self.centerContainerView setFrame:newFrame]; - [self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0]; + [self updateDrawerVisualStateForDrawerSide:visibleSide percentVisible:0.0]; } completion:^(BOOL finished) { - //End the appearance transition if it already wasn't open. - if(drawerSide != self.openSide){ - [sideDrawerViewController endAppearanceTransition]; - } - [self setOpenSide:drawerSide]; - - [self resetDrawerVisualStateForDrawerSide:drawerSide]; - + [sideDrawerViewController endAppearanceTransition]; + [self setOpenSide:MMDrawerSideNone]; + [self resetDrawerVisualStateForDrawerSide:visibleSide]; + [self setAnimatingDrawer:NO]; if(completion){ completion(finished); } @@ -288,6 +249,69 @@ -(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity: } } +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + + [self openDrawerSide:drawerSide animated:animated velocity:self.animationVelocity animationOptions:UIViewAnimationOptionCurveEaseInOut completion:completion]; +} + +-(void)openDrawerSide:(MMDrawerSide)drawerSide animated:(BOOL)animated velocity:(CGFloat)velocity animationOptions:(UIViewAnimationOptions)options completion:(void (^)(BOOL finished))completion{ + NSParameterAssert(drawerSide != MMDrawerSideNone); + if(self.isAnimatingDrawer){ + if(completion){ + completion(NO); + } + } + else { + UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:drawerSide]; + CGRect visibleRect = CGRectIntersection(self.view.bounds,sideDrawerViewController.view.frame); + BOOL drawerFullyCovered = (CGRectContainsRect(self.centerContainerView.frame, visibleRect) || + CGRectIsNull(visibleRect)); + if(drawerFullyCovered){ + [self prepareToPresentDrawer:drawerSide animated:animated]; + } + + if(sideDrawerViewController){ + self.animatingDrawer = animated; + CGRect newFrame; + CGRect oldFrame = self.centerContainerView.frame; + if(drawerSide == MMDrawerSideLeft){ + newFrame = self.centerContainerView.frame; + newFrame.origin.x = self.maximumLeftDrawerWidth; + } + else { + newFrame = self.centerContainerView.frame; + newFrame.origin.x = 0-self.maximumRightDrawerWidth; + } + + CGFloat distance = ABS(CGRectGetMinX(oldFrame)-newFrame.origin.x); + NSTimeInterval duration = MAX(distance/ABS(velocity),MMDrawerMinimumAnimationDuration); + + [UIView + animateWithDuration:(animated?duration:0.0) + delay:0.0 + options:options + animations:^{ + [self.centerContainerView setFrame:newFrame]; + [self updateDrawerVisualStateForDrawerSide:drawerSide percentVisible:1.0]; + } + completion:^(BOOL finished) { + //End the appearance transition if it already wasn't open. + if(drawerSide != self.openSide){ + [sideDrawerViewController endAppearanceTransition]; + } + [self setOpenSide:drawerSide]; + + [self resetDrawerVisualStateForDrawerSide:drawerSide]; + [self setAnimatingDrawer:NO]; + if(completion){ + completion(finished); + } + }]; + } + } +} + #pragma mark - Updating the Center View Controller -(void)setCenterViewController:(UIViewController *)centerViewController animated:(BOOL)animated{ if(_centerContainerView == nil){ @@ -327,11 +351,8 @@ -(void)setCenterViewController:(UIViewController *)centerViewController animated } } --(void)setCenterViewController:(UIViewController *)newCenterViewController withCloseAnimation:(BOOL)animated completion:(void(^)(BOOL))completion{ - UIViewController * currentCenterViewController = self.centerViewController; - [currentCenterViewController beginAppearanceTransition:NO animated:NO]; +-(void)setCenterViewController:(UIViewController *)newCenterViewController withCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{ [self setCenterViewController:newCenterViewController animated:animated]; - [currentCenterViewController endAppearanceTransition]; if(self.openSide != MMDrawerSideNone){ [self updateDrawerVisualStateForDrawerSide:self.openSide percentVisible:1.0]; @@ -356,8 +377,9 @@ -(void)setCenterViewController:(UIViewController *)newCenterViewController withC } } --(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)animated completion:(void(^)(BOOL))completion{ - if(self.openSide != MMDrawerSideNone){ +-(void)setCenterViewController:(UIViewController *)newCenterViewController withFullCloseAnimation:(BOOL)animated completion:(void(^)(BOOL finished))completion{ + if(self.openSide != MMDrawerSideNone && + animated){ UIViewController * sideDrawerViewController = [self sideDrawerViewControllerForSide:self.openSide]; @@ -420,6 +442,9 @@ -(void)setCenterViewController:(UIViewController *)newCenterViewController withF } else { [self setCenterViewController:newCenterViewController animated:animated]; + if(self.openSide != MMDrawerSideNone){ + [self closeDrawerAnimated:animated completion:completion]; + } } } @@ -524,6 +549,16 @@ -(void)setDrawerVisualStateBlock:(void (^)(MMDrawerController *, MMDrawerSide, C [self setDrawerVisualState:drawerVisualStateBlock]; } +#pragma mark - Setting Custom Gesture Handler Block +-(void)setGestureShouldRecognizeTouchBlock:(BOOL (^)(MMDrawerController *, UIGestureRecognizer *, UITouch *))gestureShouldRecognizeTouchBlock{ + [self setGestureShouldRecognizeTouch:gestureShouldRecognizeTouchBlock]; +} + +#pragma mark - Setting the Gesture Completion Block +-(void)setGestureCompletionBlock:(void (^)(MMDrawerController *, UIGestureRecognizer *))gestureCompletionBlock{ + [self setGestureCompletion:gestureCompletionBlock]; +} + #pragma mark - Subclass Methods -(BOOL)shouldAutomaticallyForwardAppearanceMethods{ return NO; @@ -537,6 +572,16 @@ -(BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers{ return NO; } +#pragma mark - View Lifecycle + +- (void)viewDidLoad { + [super viewDidLoad]; + + [self.view setBackgroundColor:[UIColor blackColor]]; + + [self setupGestureRecognizers]; +} + -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [self.centerViewController beginAppearanceTransition:YES animated:animated]; @@ -737,7 +782,11 @@ -(CGFloat)visibleRightDrawerWidth{ -(void)tapGesture:(UITapGestureRecognizer *)tapGesture{ if(self.openSide != MMDrawerSideNone){ - [self closeDrawerAnimated:YES completion:nil]; + [self closeDrawerAnimated:YES completion:^(BOOL finished) { + if(self.gestureCompletion){ + self.gestureCompletion(self, tapGesture); + } + }]; } } @@ -788,7 +837,11 @@ -(void)panGesture:(UIPanGestureRecognizer *)panGesture{ case UIGestureRecognizerStateEnded:{ self.startingPanRect = CGRectNull; CGPoint velocity = [panGesture velocityInView:self.view]; - [self finishAnimationForPanGestureWithXVelocity:velocity.x completion:nil]; + [self finishAnimationForPanGestureWithXVelocity:velocity.x completion:^(BOOL finished) { + if(self.gestureCompletion){ + self.gestureCompletion(self, panGesture); + } + }]; break; } default: @@ -833,6 +886,11 @@ -(void)finishAnimationForPanGestureWithXVelocity:(CGFloat)xVelocity completion:( [self openDrawerSide:MMDrawerSideRight animated:YES completion:completion]; } } + else { + if(completion){ + completion(NO); + } + } } -(void)updateDrawerVisualStateForDrawerSide:(MMDrawerSide)drawerSide percentVisible:(CGFloat)percentVisible{ @@ -976,22 +1034,24 @@ -(UIViewController*)sideDrawerViewControllerForSide:(MMDrawerSide)drawerSide{ #pragma mark - UIGestureRecognizerDelegate -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{ - CGPoint point = [touch locationInView:self.view]; + BOOL shouldReceiveTouch = NO; if(self.openSide == MMDrawerSideNone){ MMOpenDrawerGestureMode possibleOpenGestureModes = [self possibleOpenGestureModesForGestureRecognizer:gestureRecognizer - withTouchPoint:point]; + withTouch:touch]; return ((self.openDrawerGestureModeMask & possibleOpenGestureModes)>0); } else{ MMCloseDrawerGestureMode possibleCloseGestureModes = [self possibleCloseGestureModesForGestureRecognizer:gestureRecognizer - withTouchPoint:point]; + withTouch:touch]; return ((self.closeDrawerGestureModeMask & possibleCloseGestureModes)>0); } + return shouldReceiveTouch; } #pragma mark Gesture Recogizner Delegate Helpers --(MMCloseDrawerGestureMode)possibleCloseGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouchPoint:(CGPoint)point{ +-(MMCloseDrawerGestureMode)possibleCloseGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouch:(UITouch*)touch{ + CGPoint point = [touch locationInView:self.view]; MMCloseDrawerGestureMode possibleCloseGestureModes = MMCloseDrawerGestureModeNone; if([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){ if([self isPointContainedWithinNavigationRect:point]){ @@ -1008,7 +1068,12 @@ -(MMCloseDrawerGestureMode)possibleCloseGestureModesForGestureRecognizer:(UIGest if([self isPointContainedWithinCenterViewContentRect:point]){ possibleCloseGestureModes |= MMCloseDrawerGestureModePanningCenterView; } - if([self isPointContainedWithinBezelRect:point]){ + if([self isPointContainedWithRightBezelRect:point] && + self.openSide == MMDrawerSideLeft){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithinLeftBezelRect:point] && + self.openSide == MMDrawerSideRight){ possibleCloseGestureModes |= MMCloseDrawerGestureModeBezelPanningCenterView; } if([self isPointContainedWithinCenterViewContentRect:point] == NO && @@ -1016,10 +1081,17 @@ -(MMCloseDrawerGestureMode)possibleCloseGestureModesForGestureRecognizer:(UIGest possibleCloseGestureModes |= MMCloseDrawerGestureModePanningDrawerView; } } + if((self.closeDrawerGestureModeMask & MMCloseDrawerGestureModeCustom) > 0 && + self.gestureShouldRecognizeTouch){ + if(self.gestureShouldRecognizeTouch(self,gestureRecognizer,touch)){ + possibleCloseGestureModes |= MMCloseDrawerGestureModeCustom; + } + } return possibleCloseGestureModes; } --(MMOpenDrawerGestureMode)possibleOpenGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouchPoint:(CGPoint)point{ +-(MMOpenDrawerGestureMode)possibleOpenGestureModesForGestureRecognizer:(UIGestureRecognizer*)gestureRecognizer withTouch:(UITouch*)touch{ + CGPoint point = [touch locationInView:self.view]; MMOpenDrawerGestureMode possibleOpenGestureModes = MMOpenDrawerGestureModeNone; if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]){ if([self isPointContainedWithinNavigationRect:point]){ @@ -1028,14 +1100,24 @@ -(MMOpenDrawerGestureMode)possibleOpenGestureModesForGestureRecognizer:(UIGestur if([self isPointContainedWithinCenterViewContentRect:point]){ possibleOpenGestureModes |= MMOpenDrawerGestureModePanningCenterView; } - if([self isPointContainedWithinBezelRect:point]){ + if([self isPointContainedWithinLeftBezelRect:point] && + self.leftDrawerViewController){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeBezelPanningCenterView; + } + if([self isPointContainedWithRightBezelRect:point] && + self.rightDrawerViewController){ possibleOpenGestureModes |= MMOpenDrawerGestureModeBezelPanningCenterView; } } + if((self.openDrawerGestureModeMask & MMOpenDrawerGestureModeCustom) > 0 && + self.gestureShouldRecognizeTouch){ + if(self.gestureShouldRecognizeTouch(self,gestureRecognizer,touch)){ + possibleOpenGestureModes |= MMOpenDrawerGestureModeCustom; + } + } return possibleOpenGestureModes; } - -(BOOL)isPointContainedWithinNavigationRect:(CGPoint)point{ CGRect navigationBarRect = CGRectNull; if([self.centerViewController isKindOfClass:[UINavigationController class]]){ @@ -1053,17 +1135,20 @@ -(BOOL)isPointContainedWithinCenterViewContentRect:(CGPoint)point{ [self isPointContainedWithinNavigationRect:point] == NO); } --(BOOL)isPointContainedWithinBezelRect:(CGPoint)point{ - CGRect leftBezelRect; +-(BOOL)isPointContainedWithinLeftBezelRect:(CGPoint)point{ + CGRect leftBezelRect = CGRectNull; CGRect tempRect; CGRectDivide(self.view.bounds, &leftBezelRect, &tempRect, MMDrawerBezelRange, CGRectMinXEdge); - - CGRect rightBezelRect; + return (CGRectContainsPoint(leftBezelRect, point) && + [self isPointContainedWithinCenterViewContentRect:point]); +} + +-(BOOL)isPointContainedWithRightBezelRect:(CGPoint)point{ + CGRect rightBezelRect = CGRectNull; + CGRect tempRect; CGRectDivide(self.view.bounds, &rightBezelRect, &tempRect, MMDrawerBezelRange, CGRectMaxXEdge); - return ((CGRectContainsPoint(leftBezelRect, point) || - CGRectContainsPoint(rightBezelRect, point)) && - [self isPointContainedWithinCenterViewContentRect:point]); + return (CGRectContainsPoint(rightBezelRect, point) && + [self isPointContainedWithinCenterViewContentRect:point]); } - @end diff --git a/MMDrawerController/MMDrawerVisualState.m b/MMDrawerController/MMDrawerVisualState.m index 1a366c6b..1d58e404 100644 --- a/MMDrawerController/MMDrawerVisualState.m +++ b/MMDrawerController/MMDrawerVisualState.m @@ -32,7 +32,7 @@ +(MMDrawerControllerDrawerVisualStateBlock)slideAndScaleVisualStateBlock{ CGFloat maxDistance = 50; CGFloat distance = maxDistance * percentVisible; - CATransform3D translateTransform; + CATransform3D translateTransform = CATransform3DIdentity; UIViewController * sideDrawerViewController; if(drawerSide == MMDrawerSideLeft) { sideDrawerViewController = drawerController.leftDrawerViewController; @@ -117,7 +117,7 @@ +(MMDrawerControllerDrawerVisualStateBlock)parallaxVisualStateBlockWithParallaxF MMDrawerControllerDrawerVisualStateBlock visualStateBlock = ^(MMDrawerController * drawerController, MMDrawerSide drawerSide, CGFloat percentVisible){ NSParameterAssert(parallaxFactor >= 1.0); - CATransform3D transform; + CATransform3D transform = CATransform3DIdentity; UIViewController * sideDrawerViewController; if(drawerSide == MMDrawerSideLeft) { sideDrawerViewController = drawerController.leftDrawerViewController; diff --git a/MMDrawerController/UIViewController+MMDrawerController.m b/MMDrawerController/UIViewController+MMDrawerController.m index 7ffc0542..d2ffe3b8 100644 --- a/MMDrawerController/UIViewController+MMDrawerController.m +++ b/MMDrawerController/UIViewController+MMDrawerController.m @@ -25,16 +25,14 @@ @implementation UIViewController (MMDrawerController) -(MMDrawerController*)mm_drawerController{ - if([self.parentViewController isKindOfClass:[MMDrawerController class]]){ - return (MMDrawerController*)self.parentViewController; - } - else if([self.parentViewController isKindOfClass:[UINavigationController class]] && - [self.parentViewController.parentViewController isKindOfClass:[MMDrawerController class]]){ - return (MMDrawerController*)[self.parentViewController parentViewController]; - } - else{ - return nil; + UIViewController *parentViewController = self.parentViewController; + while (parentViewController != nil) { + if([parentViewController isKindOfClass:[MMDrawerController class]]){ + return (MMDrawerController *)parentViewController; + } + parentViewController = parentViewController.parentViewController; } + return nil; } -(CGRect)mm_visibleDrawerFrame{ diff --git a/README.md b/README.md index b7f4547a..71e84ecc 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,17 @@

- -

-
by MacBuildServer -

- - - --- ##Documentation Official appledoc documentation can be found at [CocoaDocs](http://cocoadocs.org/docsets/MMDrawerController/). --- ##Installing MMDrawerController +
You can install MMDrawerController in your project by using [CocoaPods](https://github.com/cocoapods/cocoapods): ```Ruby -pod 'MMDrawerController', '~> 0.1.0' +pod 'MMDrawerController', '~> 0.3.0' ``` --- @@ -69,6 +63,7 @@ The example project `MMDrawerControllerKitchenSinkStoryboard` demonstrates this * **MMOpenDrawerGestureModePanningNavigationBar**: The user can open the drawer by panning anywhere on the navigation bar. * **MMOpenDrawerGestureModePanningCenterView**: The user can open the drawer by panning anywhere on the center view. * **MMOpenDrawerGestureModeBezelPanningCenterView**: The user can open the drawer by starting a pan anywhere within 20 points of the bezel. + * **MMOpenDrawerGestureModeCustom**: The developer can provide a callback block to determine if the gesture should be recognized. More information below. * **MMCloseDrawerGestureMode** * **MMCloseDrawerGestureModePanningNavigationBar**: The user can close the drawer by panning anywhere on the navigation bar. @@ -77,9 +72,30 @@ The example project `MMDrawerControllerKitchenSinkStoryboard` demonstrates this * **MMCloseDrawerGestureModeTapNavigationBar**: The user can close the drawer by tapping the navigation bar. * **MMCloseDrawerGestureModeTapCenterView**: The user can close the drawer by tapping the center view. * **MMCloseDrawerGestureModePanningDrawerView**: The user can close the drawer by panning anywhere on the drawer view. + * **MMCloseDrawerGestureModeCustom**: The developer can provide a callback block to determine if the gesture should be recognized. More information below. You are free to set whatever combination you want for opening and closing. Note that these gestures may impact touches sent to the child view controllers, so be sure to use these appropriately for your application. For example, you wouldn't want `MMOpenDrawerGestureModePanningCenterView` set if a `MKMapView` is your center view controller, since it would intercept the pan meant for moving around the map. +####Custom Gesture Recognizer Support +Starting with version 0.3.0, you can now provide a callback block to determine if a gesture should be recognized using the `setGestureShouldRecognizeTouchBlock:` method. This method provides three parameters - the drawer controller, the gesture, and the touch. As a developer, you are responsible for inspecting those elements and determining if the gesture should be recognized or not. Note the block is only consulted if you have set `MMOpenDrawerGestureModeCustom`/`MMCloseDrawerGestureModeCustom` on the appropriate mask. + +For example, lets say you have a center view controller that contains a few elements, and you only want the pan gesture to be recognized to open the drawer when the touch begins within a certain subview. You would make sure that the `openDrawerGestureModeMask` contains `MMOpenDrawerGestureModeCustom`, and you could set a block below as so: + +```Objective-C +[myDrawerController + setGestureShouldRecognizeTouchBlock:^BOOL(MMDrawerController *drawerController, UIGestureRecognizer *gesture, UITouch *touch) { + BOOL shouldRecognizeTouch = NO; + if(drawerController.openSide == MMDrawerSideNone && + [gesture isKindOfClass:[UIPanGestureRecognizer class]]){ + UIView * customView = [drawerController.centerViewController myCustomSubview]; + CGPoint location = [touch locationInView:customView]; + shouldRecognizeTouch = (CGRectContainsPoint(customView.bounds, location)); + } + return shouldRecognizeTouch; + }]; + ``` + Note that you would not want the `openDrawerGestureModeMask` to contain `MMOpenDrawerGestureModePanningCenterView`, since that would take over and be applied automatically regardless of where the touch begins within the center view. + ###Custom Drawer Open/Close Animations `MMDrawerController` provides a callback block that allows you to implement your own custom state for the drawer controller when an open/close or pan gesture event happens. Within the block, you are responsible for updating the visual state of the drawer controller, and the drawer controller will handle animating to that state. @@ -95,7 +111,7 @@ For example, to set the alpha of the side drawer controller from 0 to 1 during a else if(drawerSide == MMDrawerSideRight){ sideDrawerViewController = drawerController.rightDrawerViewController; } - [sideDrawerViewController setAlpha:percentVisible]; + [sideDrawerViewController.view setAlpha:percentVisible]; }]; ```