Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/pop off on drag right #40

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Example/PSStackedViewExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
1F82478114EB76AD00773D9B /* popIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 1F82478014EB76AD00773D9B /* popIcon.png */; };
1FEE7FF814C8E23200424F88 /* PSStackedViewSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 1FEE7FF714C8E23200424F88 /* PSStackedViewSegue.m */; };
3798AD7B13E0B299004C1E33 /* error.png in Resources */ = {isa = PBXBuildFile; fileRef = 3798AD7413E0B299004C1E33 /* error.png */; };
3798AD7D13E0B299004C1E33 /* [email protected] in Resources */ = {isa = PBXBuildFile; fileRef = 3798AD7613E0B299004C1E33 /* [email protected] */; };
Expand Down Expand Up @@ -39,6 +40,7 @@
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
1F82478014EB76AD00773D9B /* popIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = popIcon.png; sourceTree = "<group>"; };
1FEE7FF614C8E23200424F88 /* PSStackedViewSegue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PSStackedViewSegue.h; path = ../../PSStackedView/PSStackedViewSegue.h; sourceTree = "<group>"; };
1FEE7FF714C8E23200424F88 /* PSStackedViewSegue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PSStackedViewSegue.m; path = ../../PSStackedView/PSStackedViewSegue.m; sourceTree = "<group>"; };
3798AD7413E0B299004C1E33 /* error.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = error.png; sourceTree = "<group>"; };
Expand Down Expand Up @@ -209,6 +211,7 @@
7884393E13CEF82D00D18A56 /* Images */ = {
isa = PBXGroup;
children = (
1F82478014EB76AD00773D9B /* popIcon.png */,
3798AD7313E0B299004C1E33 /* error */,
3798AD7713E0B299004C1E33 /* NewGlow */,
78BA6A1313CF05C200DDA16E /* 08-chat.png */,
Expand Down Expand Up @@ -283,6 +286,7 @@
3798AD7D13E0B299004C1E33 /* [email protected] in Resources */,
3798AD7E13E0B299004C1E33 /* NewGlow.png in Resources */,
3798AD8013E0B299004C1E33 /* [email protected] in Resources */,
1F82478114EB76AD00773D9B /* popIcon.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
3 changes: 3 additions & 0 deletions Example/StackedViewKitExample/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// set root controller as stack controller
ExampleMenuRootController *menuController = [[ExampleMenuRootController alloc] init];
self.stackController = [[PSStackedViewController alloc] initWithRootViewController:menuController];
self.stackController.enablePopOffOnDragRight = YES;
self.stackController.popOffType = SVPopOptionAllButFirst;
self.stackController.delegate = menuController;
self.window.rootViewController = self.stackController;
[self.window makeKeyAndVisible];

Expand Down
4 changes: 3 additions & 1 deletion Example/StackedViewKitExample/ExampleMenuRootController.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@

#import <UIKit/UIKit.h>

@interface ExampleMenuRootController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
@interface ExampleMenuRootController : UIViewController <UITableViewDataSource, UITableViewDelegate, PSStackedViewDelegate> {
UITableView *menuTable_;
UIImageView *popIconLeft_;
UIImageView *popIconRight_;
NSArray *cellContents_;
}

Expand Down
53 changes: 53 additions & 0 deletions Example/StackedViewKitExample/ExampleMenuRootController.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@
@interface ExampleMenuRootController()
@property (nonatomic, strong) UITableView *menuTable;
@property (nonatomic, strong) NSArray *cellContents;
@property (nonatomic, strong) UIImageView *popIconLeft;
@property (nonatomic, strong) UIImageView *popIconRight;
@end

@implementation ExampleMenuRootController

@synthesize menuTable = menuTable_;
@synthesize cellContents = cellContents_;
@synthesize popIconLeft = popIconLeft_;
@synthesize popIconRight = popIconRight_;

///////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark - NSObject
Expand Down Expand Up @@ -69,6 +73,16 @@ - (void)viewDidLoad {
self.menuTable.dataSource = self;
[self.view addSubview:self.menuTable];
[self.menuTable reloadData];

self.popIconLeft = [[UIImageView alloc] initWithFrame:CGRectMake(225, 482, 50, 70)];
self.popIconLeft.image = [UIImage imageNamed:@"popIcon.png"];
self.popIconLeft.alpha = 0.0;
[self.view addSubview:self.popIconLeft];

self.popIconRight = [[UIImageView alloc] initWithFrame:CGRectMake(245, 502, 50, 70)];
self.popIconRight.image = [UIImage imageNamed:@"popIcon.png"];
self.popIconRight.alpha = 0.0;
[self.view addSubview:self.popIconRight];
}

///////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -145,4 +159,43 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
}
}

/// PSStackedViewDelegate methods

- (void)stackedViewDidStartDragging:(PSStackedViewController *)stackedView {
if([stackedView.viewControllers count] > 1) {
[UIView animateWithDuration:0.2 animations:^(void) {
self.popIconLeft.alpha = 1.0;
self.popIconRight.alpha = 1.0;
}];
}
}

- (void)stackedViewDidStopDragging:(PSStackedViewController *)stackedView {
[UIView animateWithDuration:0.2 animations:^(void) {
self.popIconLeft.alpha = 0.0;
self.popIconRight.alpha = 0.0;
self.popIconRight.transform = CGAffineTransformIdentity;
}];
}

-(void)stackedView:(PSStackedViewController *)stackedView WillPopViewControllers:(NSArray *)controllers {
if([controllers count] > 0) {
[UIView animateWithDuration:0.2 animations:^(void) {
self.popIconRight.alpha = 0.5;
CGAffineTransform trans = CGAffineTransformMakeTranslation(40, 10);
trans = CGAffineTransformRotate(trans, M_PI/4);
self.popIconRight.transform = trans;
}];
}
}

- (void)stackedView:(PSStackedViewController *)stackedView WillNotPopViewControllers:(NSArray *)controllers {
if([controllers count] > 0) {
[UIView animateWithDuration:0.2 animations:^(void) {
self.popIconRight.alpha = 1.0;
self.popIconRight.transform = CGAffineTransformIdentity;
}];
}
}

@end
Binary file added Example/StackedViewKitExample/Images/popIcon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion PSStackedView/PSStackedViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@
enum {
SVSnapOptionNearest,
SVSnapOptionLeft,
SVSnapOptionRight
SVSnapOptionRight,
SVSnapOptionPopRight
} typedef PSSVSnapOption;

enum {
SVPopOptionAllButFirst,
SVPopOptionAll,
SVPopOptionTop
} typedef PSSVPopOption;

/// StackController hosing a backside rootViewController and the stacked controllers
@interface PSStackedViewController : UIViewController

Expand Down Expand Up @@ -111,6 +118,15 @@ enum {
/// Property to disable bounces
@property(nonatomic, assign) BOOL enableBounces;

/// Property to enable poping off all of the stack views except the first when dragged past a specified amount
@property(nonatomic, assign) BOOL enablePopOffOnDragRight;

/// Property to determine the type of pop off action that will be taken when entire stack is dragged to the right
@property(nonatomic, assign) PSSVPopOption popOffType;

/// Property to determine the distance the stack has to be dragged to the right to trigger popOff
@property(nonatomic, assign) NSInteger popOffDragDistance;

/// left inset thats always visible. Defaults to 60.
@property(nonatomic, assign) NSUInteger leftInset;
/// animate setting of the left inset that is always visible
Expand Down
80 changes: 78 additions & 2 deletions PSStackedView/PSStackedViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#define kPSSVStackAnimationPopDuration kPSSVStackAnimationSpeedModifier * 0.25f
#define kPSSVMaxSnapOverOffset 20
#define kPSSVAssociatedBaseViewControllerKey @"kPSSVAssociatedBaseViewController"
#define kPSSVDefaultPopOffDragDistance 150

// reduces alpha over overlapped view controllers. 1.f would totally black-out on complete overlay
#define kAlphaReductRatio 10.f
Expand All @@ -35,11 +36,18 @@ @interface PSStackedViewController() <UIGestureRecognizerDelegate> {
BOOL lastDragDividedOne_;
NSInteger lastVisibleIndexBeforeRotation_;
BOOL enableBounces_;
BOOL enablePopOffOnDragRight_;
PSSVPopOption popOffType_;
NSInteger popOffDragDistance_;
struct {
unsigned int delegateWillInsertViewController:1;
unsigned int delegateDidInsertViewController:1;
unsigned int delegateWillRemoveViewController:1;
unsigned int delegateDidRemoveViewController:1;
unsigned int delegateDidRemoveViewController:1;
unsigned int delegateDidStartDragging:1;
unsigned int delegateDidStopDragging:1;
unsigned int delegateWillPopViewControllers:1;
unsigned int delegateWillNotPopViewControllers:1;
}delegateFlags_;
}
@property(nonatomic, strong) UIViewController *rootViewController;
Expand All @@ -60,6 +68,9 @@ @implementation PSStackedViewController
@synthesize delegate = delegate_;
@synthesize reduceAnimations = reduceAnimations_;
@synthesize enableBounces = enableBounces_;
@synthesize enablePopOffOnDragRight = enablePopOffOnDragRight_;
@synthesize popOffType = popOffType_;
@synthesize popOffDragDistance = popOffDragDistance_;
@dynamic firstVisibleIndex;

#ifdef ALLOW_SWIZZLING_NAVIGATIONCONTROLLER
Expand All @@ -79,6 +90,7 @@ - (id)initWithRootViewController:(UIViewController *)rootViewController; {
// set some reasonble defaults
leftInset_ = 60;
largeLeftInset_ = 200;
popOffDragDistance_ = kPSSVDefaultPopOffDragDistance;

// add a gesture recognizer to detect dragging to the guest controllers
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanFrom:)];
Expand Down Expand Up @@ -123,6 +135,10 @@ - (void)setDelegate:(id<PSStackedViewDelegate>)delegate {
delegateFlags_.delegateDidInsertViewController = [delegate respondsToSelector:@selector(stackedView:didInsertViewController:)];
delegateFlags_.delegateWillRemoveViewController = [delegate respondsToSelector:@selector(stackedView:willRemoveViewController:)];
delegateFlags_.delegateDidRemoveViewController = [delegate respondsToSelector:@selector(stackedView:didRemoveViewController:)];
delegateFlags_.delegateDidStartDragging = [delegate respondsToSelector:@selector(stackedViewDidStartDragging:)];
delegateFlags_.delegateDidStopDragging = [delegate respondsToSelector:@selector(stackedViewDidStopDragging:)];
delegateFlags_.delegateWillPopViewControllers = [delegate respondsToSelector:@selector(stackedView:WillPopViewControllers:)];
delegateFlags_.delegateWillNotPopViewControllers = [delegate respondsToSelector:@selector(stackedView:WillNotPopViewControllers:)];
}
}

Expand Down Expand Up @@ -775,6 +791,9 @@ - (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {

// set up designated drag destination
if (state == UIGestureRecognizerStateBegan) {
if (delegateFlags_.delegateDidStartDragging) {
[delegate_ stackedViewDidStartDragging:self];
}
if (offset > 0) {
lastDragOption_ = SVSnapOptionRight;
}else {
Expand All @@ -792,6 +811,44 @@ - (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
lastDragOffset_ = translatedPoint.x;
}

if(self.enablePopOffOnDragRight && [viewControllers_ count] > 0) {
UIViewController* fvc = (UIViewController*)[viewControllers_ objectAtIndex:0];
NSInteger currentDragDistance = (fvc.containerView.left - largeLeftInset_);

if(currentDragDistance > popOffDragDistance_ && lastDragOption_ != SVSnapOptionPopRight) {
lastDragOption_ = SVSnapOptionPopRight;
if(delegateFlags_.delegateWillPopViewControllers) {
NSArray* toPop;
if(self.popOffType == SVPopOptionAll) {
toPop = [self.viewControllers copy];
}
else if(self.popOffType == SVPopOptionAllButFirst) {
toPop = [self.viewControllers subarrayWithRange:NSMakeRange(1, [self.viewControllers count] - 1)];
}
else if(self.popOffType == SVPopOptionTop) {
toPop = [self.viewControllers subarrayWithRange:NSMakeRange([self.viewControllers count] - 1, 1)];
}
[delegate_ stackedView:self WillPopViewControllers:toPop];
}
}
else if(currentDragDistance < popOffDragDistance_ && lastDragOption_ == SVSnapOptionPopRight) {
lastDragOption_ = SVSnapOptionNearest;
if(delegateFlags_.delegateWillNotPopViewControllers) {
NSArray* toPop;
if(self.popOffType == SVPopOptionAll) {
toPop = [self.viewControllers copy];
}
else if(self.popOffType == SVPopOptionAllButFirst) {
toPop = [self.viewControllers subarrayWithRange:NSMakeRange(1, [self.viewControllers count] - 1)];
}
else if(self.popOffType == SVPopOptionTop) {
toPop = [self.viewControllers subarrayWithRange:NSMakeRange([self.viewControllers count] - 1, 1)];
}
[delegate_ stackedView:self WillNotPopViewControllers:toPop];
}
}
}

// perform snapping after gesture ended
BOOL gestureEnded = state == UIGestureRecognizerStateEnded;
if (gestureEnded) {
Expand All @@ -800,11 +857,30 @@ - (void)handlePanFrom:(UIPanGestureRecognizer *)recognizer {
self.floatIndex = [self nearestValidFloatIndex:self.floatIndex round:PSSVRoundDown];
}else if(lastDragOption_ == SVSnapOptionLeft) {
self.floatIndex = [self nearestValidFloatIndex:self.floatIndex round:PSSVRoundUp];
}else {
}else if(lastDragOption_ == SVSnapOptionPopRight) {
self.floatIndex = 0.0;
if(self.popOffType == SVPopOptionAll) {
[self popToRootViewControllerAnimated:YES];
}
else if(self.popOffType == SVPopOptionAllButFirst) {
[self popToViewController:[self.viewControllers objectAtIndex:0] animated:YES];
}
else if(self.popOffType == SVPopOptionTop) {
if([self.viewControllers count] == 1)
[self popToRootViewControllerAnimated:YES];
else
[self popToViewController:[self.viewControllers objectAtIndex:[self.viewControllers count]-2] animated:YES];
}
}
else {
self.floatIndex = [self nearestValidFloatIndex:self.floatIndex round:PSSVRoundNearest];
}

[self alignStackAnimated:YES];

if(delegateFlags_.delegateDidStopDragging) {
[delegate_ stackedViewDidStopDragging:self];
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions PSStackedView/PSStackedViewDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,16 @@
/// viewController has been removed
- (void)stackedView:(PSStackedViewController *)stackedView didRemoveViewController:(UIViewController *)viewController;

/// stackcontroller will pop off view controllers because of drag to right
- (void)stackedView:(PSStackedViewController*)stackedView WillPopViewControllers:(NSArray*)controllers;

/// stackcontroller will no longer pop off view controllers because of drag to right
- (void)stackedView:(PSStackedViewController *)stackedView WillNotPopViewControllers:(NSArray*)controllers;

/// stackcontroller did start dragging stack
- (void)stackedViewDidStartDragging:(PSStackedViewController*)stackedView;

/// stackcontroller did stop dragging stack
- (void)stackedViewDidStopDragging:(PSStackedViewController*)stackedView;

@end
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ window.rootViewController = self.stackController;

PSStackedViewRootController's rootViewController is in the background and its left part is always visible. Adjust the size with leftInset and largeLeftInset.

PS: Added "Remove top view controller if user slides stack to the right"

Code by: [mball-crrc](https://github.com/mball-crrc/PSStackedView/tree/feature/pop_off_on_drag_right)

Pull merge by: [fabiosoft](https://github.com/fabiosoft) - [website](http://www.fabiosoft.com)

## Roadmap
- Add (conditional) support for the new child view controller system in iOS5
- Appledoc
Expand Down