From b6b9da91ed4cd562d2b931057c86aa7d3a090ce3 Mon Sep 17 00:00:00 2001 From: Mike Enriquez Date: Mon, 30 Jan 2012 15:39:52 -0500 Subject: [PATCH] New API with documentation --- ECSlidingViewController/AppDelegate.m | 3 +- .../FirstTopViewController.h | 2 + .../FirstTopViewController.m | 42 ++---- ECSlidingViewController/MenuViewController.m | 12 +- .../SecondTopViewController.h | 1 + .../SecondTopViewController.m | 13 +- .../ThirdTopViewController.h | 2 + .../ThirdTopViewController.m | 17 ++- .../UnderRightViewController.m | 30 +++-- .../ECSlidingViewController.h | 115 ++++++++++++++-- .../ECSlidingViewController.m | 127 +++++++++++------- 11 files changed, 247 insertions(+), 117 deletions(-) diff --git a/ECSlidingViewController/AppDelegate.m b/ECSlidingViewController/AppDelegate.m index f2262e0..207c2f4 100644 --- a/ECSlidingViewController/AppDelegate.m +++ b/ECSlidingViewController/AppDelegate.m @@ -17,8 +17,7 @@ ECSlidingViewController *slidingViewController = (ECSlidingViewController *)self.window.rootViewController; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; - slidingViewController.underLeftViewController = [storyboard instantiateViewControllerWithIdentifier:@"Menu"]; - slidingViewController.topViewController = [storyboard instantiateViewControllerWithIdentifier:@"FirstTop"]; + slidingViewController.topViewController = [storyboard instantiateViewControllerWithIdentifier:@"FirstTop"]; return YES; } diff --git a/ECSlidingViewController/FirstTopViewController.h b/ECSlidingViewController/FirstTopViewController.h index cf0e2e9..eae7a94 100644 --- a/ECSlidingViewController/FirstTopViewController.h +++ b/ECSlidingViewController/FirstTopViewController.h @@ -9,6 +9,8 @@ #import #import #import "ECSlidingViewController.h" +#import "MenuViewController.h" +#import "UnderRightViewController.h" @interface FirstTopViewController : UIViewController diff --git a/ECSlidingViewController/FirstTopViewController.m b/ECSlidingViewController/FirstTopViewController.m index b416595..bdf4800 100644 --- a/ECSlidingViewController/FirstTopViewController.m +++ b/ECSlidingViewController/FirstTopViewController.m @@ -8,20 +8,7 @@ #import "FirstTopViewController.h" -@interface FirstTopViewController() -@property (nonatomic, unsafe_unretained) CGFloat peekRight; -@property (nonatomic, unsafe_unretained) CGFloat peekLeft; -@end - @implementation FirstTopViewController -@synthesize peekRight; -@synthesize peekLeft; - -- (void)viewDidLoad -{ - self.peekRight = 40.0f; - self.peekLeft = 40.0f; -} - (void)viewWillAppear:(BOOL)animated { @@ -34,24 +21,17 @@ self.view.layer.shadowPath = [UIBezierPath bezierPathWithRect:self.view.layer.bounds].CGPath; self.view.clipsToBounds = NO; - [self.slidingViewController enablePanningInDirection:ECSlideLeft forView:self.view peekAmount:self.peekLeft]; - [self.slidingViewController enablePanningInDirection:ECSlideRight forView:self.view peekAmount:self.peekRight]; - UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; - self.slidingViewController.underRightViewController = [storyboard instantiateViewControllerWithIdentifier:@"UnderRight"]; -} - -- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration -{ - if ([self.slidingViewController underLeftShowing] && ![self.slidingViewController topViewIsOffScreen]) { - [self.slidingViewController jumpToPeekAmount:self.peekRight inDirection:ECSlideRight]; - } else if ([self.slidingViewController underRightShowing] && ![self.slidingViewController topViewIsOffScreen]) { - [self.slidingViewController jumpToPeekAmount:self.peekLeft inDirection:ECSlideLeft]; - } else if ([self.slidingViewController underLeftShowing] && [self.slidingViewController topViewIsOffScreen]) { - [self.slidingViewController jumpToPeekAmount:0 inDirection:ECSlideRight]; - } else if ([self.slidingViewController underRightShowing] && [self.slidingViewController topViewIsOffScreen]) { - [self.slidingViewController jumpToPeekAmount:0 inDirection:ECSlideLeft]; + + if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) { + self.slidingViewController.underLeftViewController = [storyboard instantiateViewControllerWithIdentifier:@"Menu"]; } + + if (![self.slidingViewController.underRightViewController isKindOfClass:[UnderRightViewController class]]) { + self.slidingViewController.underRightViewController = [storyboard instantiateViewControllerWithIdentifier:@"UnderRight"]; + } + + [self.view addGestureRecognizer:self.slidingViewController.panGesture]; } - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration @@ -68,12 +48,12 @@ - (IBAction)revealMenu:(id)sender { - [self.slidingViewController slideInDirection:ECSlideRight peekAmount:self.peekRight onComplete:nil]; + [self.slidingViewController anchorTopViewTo:ECRight animations:nil onComplete:nil]; } - (IBAction)revealUnderRight:(id)sender { - [self.slidingViewController slideInDirection:ECSlideLeft peekAmount:self.peekLeft onComplete:nil]; + [self.slidingViewController anchorTopViewTo:ECLeft animations:nil onComplete:nil]; } @end \ No newline at end of file diff --git a/ECSlidingViewController/MenuViewController.m b/ECSlidingViewController/MenuViewController.m index 995047d..d2908c1 100644 --- a/ECSlidingViewController/MenuViewController.m +++ b/ECSlidingViewController/MenuViewController.m @@ -20,6 +20,13 @@ self.menuItems = [NSArray arrayWithObjects:@"First", @"Second", @"Third", nil]; } +- (void)viewDidLoad +{ + [super viewDidLoad]; + + [self.slidingViewController setAnchorRightPeekAmount:40.0f]; +} + - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionIndex { return self.menuItems.count; @@ -43,11 +50,12 @@ NSString *identifier = [NSString stringWithFormat:@"%@Top", [self.menuItems objectAtIndex:indexPath.row]]; UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; UIViewController *newTopViewController = [storyboard instantiateViewControllerWithIdentifier:identifier]; - [self.slidingViewController slideInDirection:ECSlideRight peekAmount:0.0f onComplete:^{ + + [self.slidingViewController anchorTopViewOffScreenTo:ECRight animations:nil onComplete:^{ CGRect frame = self.slidingViewController.topViewController.view.frame; self.slidingViewController.topViewController = newTopViewController; self.slidingViewController.topViewController.view.frame = frame; - [self.slidingViewController reset]; + [self.slidingViewController resetTopView]; }]; } diff --git a/ECSlidingViewController/SecondTopViewController.h b/ECSlidingViewController/SecondTopViewController.h index f882d08..9765c57 100644 --- a/ECSlidingViewController/SecondTopViewController.h +++ b/ECSlidingViewController/SecondTopViewController.h @@ -8,6 +8,7 @@ #import #import "ECSlidingViewController.h" +#import "MenuViewController.h" @interface SecondTopViewController : UIViewController - (IBAction)revealMenu:(id)sender; diff --git a/ECSlidingViewController/SecondTopViewController.m b/ECSlidingViewController/SecondTopViewController.m index 07e688d..a62cb59 100644 --- a/ECSlidingViewController/SecondTopViewController.m +++ b/ECSlidingViewController/SecondTopViewController.m @@ -13,12 +13,21 @@ - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - [self.slidingViewController enablePanningInDirection:ECSlideRight forView:self.view peekAmount:40.0f]; + + if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) { + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; + self.slidingViewController.underLeftViewController = [storyboard instantiateViewControllerWithIdentifier:@"Menu"]; + } + self.slidingViewController.underRightViewController = nil; + self.slidingViewController.anchorLeftPeekAmount = NSNotFound; + self.slidingViewController.anchorLeftRevealAmount = NSNotFound; + + [self.view addGestureRecognizer:self.slidingViewController.panGesture]; } - (IBAction)revealMenu:(id)sender { - [self.slidingViewController slideInDirection:ECSlideRight peekAmount:40.0f onComplete:nil]; + [self.slidingViewController anchorTopViewTo:ECRight animations:nil onComplete:nil]; } @end diff --git a/ECSlidingViewController/ThirdTopViewController.h b/ECSlidingViewController/ThirdTopViewController.h index c267399..4b115c5 100644 --- a/ECSlidingViewController/ThirdTopViewController.h +++ b/ECSlidingViewController/ThirdTopViewController.h @@ -8,6 +8,8 @@ #import #import "ECSlidingViewController.h" +#import "MenuViewController.h" +#import "UnderRightViewController.h" @interface ThirdTopViewController : UIViewController - (IBAction)revealMenu:(id)sender; diff --git a/ECSlidingViewController/ThirdTopViewController.m b/ECSlidingViewController/ThirdTopViewController.m index 1faa25c..3fdb04a 100644 --- a/ECSlidingViewController/ThirdTopViewController.m +++ b/ECSlidingViewController/ThirdTopViewController.m @@ -13,14 +13,23 @@ - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; - [self.slidingViewController enablePanningInDirection:ECSlideLeft forView:self.view peekAmount:40.0f]; - [self.slidingViewController enablePanningInDirection:ECSlideRight forView:self.view peekAmount:40.0f]; - self.slidingViewController.underRightViewController = nil; + + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Storyboard" bundle:nil]; + + if (![self.slidingViewController.underLeftViewController isKindOfClass:[MenuViewController class]]) { + self.slidingViewController.underLeftViewController = [storyboard instantiateViewControllerWithIdentifier:@"Menu"]; + } + + if (![self.slidingViewController.underRightViewController isKindOfClass:[UnderRightViewController class]]) { + self.slidingViewController.underRightViewController = [storyboard instantiateViewControllerWithIdentifier:@"UnderRight"]; + } + + [self.view addGestureRecognizer:self.slidingViewController.panGesture]; } - (IBAction)revealMenu:(id)sender { - [self.slidingViewController slideInDirection:ECSlideRight peekAmount:40.0f onComplete:nil]; + [self.slidingViewController anchorTopViewTo:ECRight animations:nil onComplete:nil]; } @end diff --git a/ECSlidingViewController/UnderRightViewController.m b/ECSlidingViewController/UnderRightViewController.m index bbe99a3..2c8cc02 100644 --- a/ECSlidingViewController/UnderRightViewController.m +++ b/ECSlidingViewController/UnderRightViewController.m @@ -9,19 +9,21 @@ #import "UnderRightViewController.h" @interface UnderRightViewController() -@property (nonatomic, unsafe_unretained) CGFloat peekAmount; +@property (nonatomic, unsafe_unretained) CGFloat peekLeftAmount; @property (nonatomic, unsafe_unretained) BOOL isSearching; - (void)updateLayoutForOrientation:(UIInterfaceOrientation)orientation; @end @implementation UnderRightViewController -@synthesize peekAmount; +@synthesize peekLeftAmount; @synthesize isSearching; - (void)viewDidLoad { - self.peekAmount = 40.0f; + [super viewDidLoad]; + self.peekLeftAmount = 40.0f; self.isSearching = NO; + [self.slidingViewController setAnchorLeftPeekAmount:self.peekLeftAmount]; } - (void)viewWillAppear:(BOOL)animated @@ -50,8 +52,8 @@ if (self.isSearching) { newLeftEdge = 0; } else { - newLeftEdge = self.peekAmount; - newWidth -= self.peekAmount; + newLeftEdge = self.peekLeftAmount; + newWidth -= self.peekLeftAmount; } frame.origin.x = newLeftEdge; @@ -62,7 +64,7 @@ - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { - [UIView animateWithDuration:0.25f animations:^{ + [self.slidingViewController anchorTopViewOffScreenTo:ECLeft animations:^{ CGRect frame = self.view.frame; frame.origin.x = 0.0f; if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) { @@ -71,25 +73,25 @@ frame.size.width = [UIScreen mainScreen].bounds.size.width; } self.view.frame = frame; + } onComplete:^{ + self.isSearching = YES; }]; - [self.slidingViewController slideInDirection:ECSlideLeft peekAmount:-1.0f onComplete:nil]; - self.isSearching = YES; } - (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { - [UIView animateWithDuration:0.25f animations:^{ + [self.slidingViewController anchorTopViewTo:ECLeft animations:^{ CGRect frame = self.view.frame; - frame.origin.x = self.peekAmount; + frame.origin.x = self.peekLeftAmount; if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) { - frame.size.width = [UIScreen mainScreen].bounds.size.height - self.peekAmount; + frame.size.width = [UIScreen mainScreen].bounds.size.height - self.peekLeftAmount; } else if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) { - frame.size.width = [UIScreen mainScreen].bounds.size.width - self.peekAmount; + frame.size.width = [UIScreen mainScreen].bounds.size.width - self.peekLeftAmount; } self.view.frame = frame; + } onComplete:^{ + self.isSearching = NO; }]; - [self.slidingViewController slideInDirection:ECSlideLeft peekAmount:self.peekAmount onComplete:nil]; - self.isSearching = NO; } @end diff --git a/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.h b/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.h index a0ffc60..6faa739 100644 --- a/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.h +++ b/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.h @@ -9,27 +9,122 @@ #import #import "UIImage+UIImage_ImageWithUIView.h" +/** @constant ECSide side of screen */ typedef enum { - ECSlideLeft, - ECSlideRight -} ECSlideDirection; + /** Left side of screen */ + ECLeft, + /** Right side of screen */ + ECRight +} ECSide; +/** @constant ECResetStrategy top view behavior while anchored. */ +typedef enum { + /** No reset strategy will be used */ + ECNone = 0, + /** Tapping the top view will reset it */ + ECTapping = 1 << 0, + /** Panning will be enabled on the top view. If it is panned and released towards the reset position it will reset, otherwise it will slide towards the anchored position. */ + ECPanning = 1 << 1 +} ECResetStrategy; + +/** ECSlidingViewController is a view controller container that presents its child view controllers in two layers. The top layer can be panned to reveal the layers below it. */ @interface ECSlidingViewController : UIViewController +/** Returns the view controller that will be visible when the top view is slide to the right. + + This view controller is typically a menu or top-level view that switches out the top view controller. + */ @property (nonatomic, strong) UIViewController *underLeftViewController; + +/** Returns the view controller that will be visible when the top view is slide to the left. + + This view controller is typically a supplemental view to the top view. + */ @property (nonatomic, strong) UIViewController *underRightViewController; + +/** Returns the top view controller. + + This is the main view controller that is presented above the other view controllers. + */ @property (nonatomic, strong) UIViewController *topViewController; -- (void)slideInDirection:(ECSlideDirection)slideDirection peekAmount:(CGFloat)peekAmount onComplete:(void(^)())completeBlock; -- (void)enablePanningInDirection:(ECSlideDirection)slideDirection forView:(UIView *)view peekAmount:(CGFloat)peekAmount; -- (void)jumpToPeekAmount:(CGFloat)peekAmount inDirection:(ECSlideDirection)direction; -- (void)reset; -- (BOOL)underLeftShowing; -- (BOOL)underRightShowing; -- (BOOL)topViewIsOffScreen; +/** Returns the number of points the top view is visible when the top view is anchored to the left side. + + This value is fixed after rotation. If the number of points to reveal needs to be fixed, use anchorLeftRevealAmount. + + @see anchorLeftRevealAmount + */ +@property (nonatomic, unsafe_unretained) CGFloat anchorLeftPeekAmount; + +/** Returns the number of points the top view is visible when the top view is anchored to the right side. + + This value is fixed after rotation. If the number of points to reveal needs to be fixed, use anchorRightRevealAmount. + + @see anchorRightRevealAmount + */ +@property (nonatomic, unsafe_unretained) CGFloat anchorRightPeekAmount; + +/** Returns the number of points the under right view is visible when the top view is anchored to the left side. + + This value is fixed after rotation. If the number of points to peek needs to be fixed, use anchorLeftPeekAmount. + + @see anchorLeftPeekAmount + */ +@property (nonatomic, unsafe_unretained) CGFloat anchorLeftRevealAmount; + +/** Returns the number of points the under left view is visible when the top view is anchored to the right side. + + This value is fixed after rotation. If the number of points to peek needs to be fixed, use anchorRightPeekAmount. + + @see anchorRightPeekAmount + */ +@property (nonatomic, unsafe_unretained) CGFloat anchorRightRevealAmount; + +/** Specifies if the user should be able to interact with the top view when it is anchored. + + By default, this is set to NO + */ +@property (nonatomic, unsafe_unretained) BOOL shouldAllowUserInteractionsWhenAnchored; + +/** Returns the strategy for resetting the top view when it is anchored. + + By default, this is set to ECPanning | ECTapping to allow both panning and tapping to reset the top view. + + If this is set to ECNone, then there must be a custom way to reset the top view otherwise it will stay anchored. + */ +@property (nonatomic, unsafe_unretained) ECResetStrategy resetStrategy; + +/** Returns a horizontal panning gesture for moving the top view. + + This is typically added to the top view or a top view's navigation bar. + */ +- (UIPanGestureRecognizer *)panGesture; + +/** Slides the top view in the direction of the specified side. + + A peek amount or reveal amount must be set for the given side. The top view will anchor to one of those specified values. + + @param side The side for the top view to slide towards. + @param animations Perform changes to properties that will be animated while top view is moved off screen. Can be nil. + @param onComplete Executed after the animation is completed. Can be nil. + */ +- (void)anchorTopViewTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete; + +/** Slides the top view off of the screen in the direction of the specified side. + + @param side The side for the top view to slide off the screen towards. + @param animations Perform changes to properties that will be animated while top view is moved off screen. Can be nil. + @param onComplete Executed after the animation is completed. Can be nil. + */ +- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete; + +/** Slides the top view back to the center. */ +- (void)resetTopView; @end +/** UIViewController extension */ @interface UIViewController(SlidingViewExtension) +/** Convience method for getting access to the ECSlidingViewController instance */ - (ECSlidingViewController *)slidingViewController; @end \ No newline at end of file diff --git a/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.m b/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.m index e6f2ba7..a520f7d 100644 --- a/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.m +++ b/ECSlidingViewController/Vendor/ECSlidingViewController/ECSlidingViewController.m @@ -10,14 +10,16 @@ @interface ECSlidingViewController() -@property (nonatomic, unsafe_unretained) CGFloat rightSidePeekAmount; -@property (nonatomic, unsafe_unretained) CGFloat leftSidePeekAmount; @property (nonatomic, strong) UIButton *topViewSnapshot; @property (nonatomic, unsafe_unretained) CGFloat initialTouchPositionX; @property (nonatomic, unsafe_unretained) CGFloat initialHoizontalCenter; @property (nonatomic, strong) UIPanGestureRecognizer *panGesture; @property (nonatomic, strong) UITapGestureRecognizer *resetTapGesture; +- (BOOL)underLeftShowing; +- (BOOL)underRightShowing; +- (BOOL)topViewIsOffScreen; + - (NSUInteger)autoResizeToFillScreen; - (UIView *)topView; - (UIView *)underLeftView; @@ -57,32 +59,34 @@ @synthesize underLeftViewController = _underLeftViewController; @synthesize underRightViewController = _underRightViewController; @synthesize topViewController = _topViewController; +@synthesize anchorLeftPeekAmount; +@synthesize anchorRightPeekAmount; +@synthesize anchorLeftRevealAmount; +@synthesize anchorRightRevealAmount; +@synthesize shouldAllowUserInteractionsWhenAnchored; +@synthesize resetStrategy; // category properties -@synthesize leftSidePeekAmount; -@synthesize rightSidePeekAmount; @synthesize topViewSnapshot; @synthesize initialTouchPositionX; @synthesize initialHoizontalCenter; -@synthesize panGesture; +@synthesize panGesture = _panGesture; @synthesize resetTapGesture; - (void)setTopViewController:(UIViewController *)theTopViewController { - self.leftSidePeekAmount = NSNotFound; - self.rightSidePeekAmount = NSNotFound; - [self removeTopViewSnapshot]; [_topViewController.view removeFromSuperview]; [_topViewController removeFromParentViewController]; _topViewController = theTopViewController; - [_topViewController.view setAutoresizingMask:self.autoResizeToFillScreen]; - [_topViewController.view setFrame:self.view.bounds]; [self addChildViewController:self.topViewController]; [self.topViewController didMoveToParentViewController:self]; + [_topViewController.view setAutoresizingMask:self.autoResizeToFillScreen]; + [_topViewController.view setFrame:self.view.bounds]; + [self.view addSubview:_topViewController.view]; } @@ -94,12 +98,12 @@ _underLeftViewController = theUnderLeftViewController; if (_underLeftViewController) { - [_underLeftViewController.view setAutoresizingMask:self.autoResizeToFillScreen]; - [_underLeftViewController.view setFrame:self.view.bounds]; - [self addChildViewController:self.underLeftViewController]; [self.underLeftViewController didMoveToParentViewController:self]; + [_underLeftViewController.view setAutoresizingMask:self.autoResizeToFillScreen]; + [_underLeftViewController.view setFrame:self.view.bounds]; + [self.view insertSubview:_underLeftViewController.view atIndex:0]; } } @@ -112,20 +116,33 @@ _underRightViewController = theUnderRightViewController; if (_underRightViewController) { - [_underRightViewController.view setAutoresizingMask:self.autoResizeToFillScreen]; - [_underRightViewController.view setFrame:self.view.bounds]; - [self addChildViewController:self.underRightViewController]; [self.underRightViewController didMoveToParentViewController:self]; + [_underRightViewController.view setAutoresizingMask:self.autoResizeToFillScreen]; + [_underRightViewController.view setFrame:self.view.bounds]; + [self.view insertSubview:_underRightViewController.view atIndex:0]; } } - (void)viewDidLoad { - self.resetTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(reset)]; - self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(updateTopViewHorizontalCenterWithRecognizer:)]; + self.resetTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(resetTopView)]; + _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(updateTopViewHorizontalCenterWithRecognizer:)]; +} + +- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration +{ + if ([self underRightShowing] && ![self topViewIsOffScreen]) { + [self updateTopViewHorizontalCenter:-self.resettedCenter + self.anchorLeftPeekAmount]; + } else if ([self underRightShowing] && [self topViewIsOffScreen]) { + [self updateTopViewHorizontalCenter:-self.resettedCenter]; + } else if ([self underLeftShowing] && ![self topViewIsOffScreen]) { + [self updateTopViewHorizontalCenter:self.screenWidth + self.resettedCenter - self.anchorRightPeekAmount]; + } else if ([self underLeftShowing] && [self topViewIsOffScreen]) { + [self updateTopViewHorizontalCenter:self.screenWidth + self.resettedCenter]; + } } - (void)updateTopViewHorizontalCenterWithRecognizer:(UIPanGestureRecognizer *)recognizer @@ -140,7 +157,7 @@ CGFloat panAmount = self.initialTouchPositionX - currentTouchPositionX; CGFloat newCenterPosition = self.initialHoizontalCenter - panAmount; - if ((newCenterPosition < self.resettedCenter && self.leftSidePeekAmount == NSNotFound) || (newCenterPosition > self.resettedCenter && self.rightSidePeekAmount == NSNotFound)) { + if ((newCenterPosition < self.resettedCenter && self.anchorLeftPeekAmount == NSNotFound) || (newCenterPosition > self.resettedCenter && self.anchorRightPeekAmount == NSNotFound)) { newCenterPosition = self.resettedCenter; } @@ -152,63 +169,69 @@ CGFloat currentVelocityX = currentVelocityPoint.x; if ([self underLeftShowing] && currentVelocityX > 100) { - [self slideInDirection:ECSlideRight peekAmount:self.rightSidePeekAmount onComplete:nil]; + [self anchorTopViewTo:ECRight animations:nil onComplete:nil]; } else if ([self underRightShowing] && currentVelocityX < 100) { - [self slideInDirection:ECSlideLeft peekAmount:self.leftSidePeekAmount onComplete:nil]; + [self anchorTopViewTo:ECLeft animations:nil onComplete:nil]; } else { - [self reset]; + [self resetTopView]; } } } -- (void)slideInDirection:(ECSlideDirection)slideDirection peekAmount:(CGFloat)peekAmount onComplete:(void(^)())completeBlock; +- (UIPanGestureRecognizer *)panGesture +{ + return _panGesture; +} + +- (void)anchorTopViewTo:(ECSide)side animations:(void (^)())animations onComplete:(void (^)())complete { CGFloat newCenter = self.topView.center.x; - if (slideDirection == ECSlideLeft) { - newCenter = -self.screenWidth + self.resettedCenter + peekAmount; - } else if (slideDirection == ECSlideRight) { - newCenter = self.screenWidth + self.resettedCenter - peekAmount; + if (side == ECLeft) { + newCenter = -self.resettedCenter + self.anchorLeftPeekAmount; + } else if (side == ECRight) { + newCenter = self.screenWidth + self.resettedCenter - self.anchorRightPeekAmount; } [self topViewHorizontalCenterWillChange:newCenter]; [UIView animateWithDuration:0.25f animations:^{ - [self jumpToPeekAmount:peekAmount inDirection:slideDirection]; - } completion:^(BOOL finished) { - if (completeBlock) { - completeBlock(); + if (animations) { + animations(); + } + [self updateTopViewHorizontalCenter:newCenter]; + } completion:^(BOOL finished){ + if (complete) { + complete(); } }]; } -- (void)enablePanningInDirection:(ECSlideDirection)slideDirection forView:(UIView *)view peekAmount:(CGFloat)peekAmount -{ - if (slideDirection == ECSlideLeft) { - self.leftSidePeekAmount = peekAmount; - } else if (slideDirection == ECSlideRight) { - self.rightSidePeekAmount = peekAmount; - } - - if (![[view gestureRecognizers] containsObject:self.panGesture]) { - [view addGestureRecognizer:self.panGesture]; - } -} - -- (void)jumpToPeekAmount:(CGFloat)peekAmount inDirection:(ECSlideDirection)slideDirection +- (void)anchorTopViewOffScreenTo:(ECSide)side animations:(void(^)())animations onComplete:(void(^)())complete { CGFloat newCenter = self.topView.center.x; - if (slideDirection == ECSlideLeft) { - newCenter = -self.screenWidth + self.resettedCenter + peekAmount; - } else if (slideDirection == ECSlideRight) { - newCenter = self.screenWidth + self.resettedCenter - peekAmount; + if (side == ECLeft) { + newCenter = -self.resettedCenter; + } else if (side == ECRight) { + newCenter = self.screenWidth + self.resettedCenter; } - [self updateTopViewHorizontalCenter:newCenter]; + [self topViewHorizontalCenterWillChange:newCenter]; + + [UIView animateWithDuration:0.25f animations:^{ + if (animations) { + animations(); + } + [self updateTopViewHorizontalCenter:newCenter]; + } completion:^(BOOL finished){ + if (complete) { + complete(); + } + }]; } -- (void)reset +- (void)resetTopView { [UIView animateWithDuration:0.25f animations:^{ [self updateTopViewHorizontalCenter:self.resettedCenter]; @@ -229,7 +252,7 @@ - (BOOL)topViewIsOffScreen { - return self.topView.center.x <= -self.resettedCenter || self.topView.center.x >= self.screenWidth + self.resettedCenter; + return self.topView.center.x <= -self.resettedCenter + 1 || self.topView.center.x >= self.screenWidth + self.resettedCenter - 1; } - (NSUInteger)autoResizeToFillScreen