├── MPFoldTransition
├── en.lproj
│ ├── InfoPlist.strings
│ └── MainStoryboard_iPhone.storyboard
├── Icon.png
├── Icon-72.png
├── Icon@2x.png
├── Icon-72@2x.png
├── MPFoldTransition
│ ├── Segues
│ │ ├── MPFoldModalSegue.h
│ │ ├── MPFoldNavPopSegue.h
│ │ ├── MPFoldNavPushSegue.h
│ │ ├── MPFoldSegue.h
│ │ ├── MPFoldModalSegue.m
│ │ ├── MPFlipSegue.h
│ │ ├── MPFoldSegue.m
│ │ ├── MPFoldNavPushSegue.m
│ │ ├── MPFoldNavPopSegue.m
│ │ └── MPFlipSegue.m
│ ├── MPAnimation.h
│ ├── MPTransitionEnumerations.h
│ ├── MPFoldEnumerations.h
│ ├── MPFlipEnumerations.h
│ ├── MPTransition.h
│ ├── MPFoldTransition.h
│ ├── MPFlipTransition.h
│ ├── MPAnimation.m
│ ├── MPTransition.m
│ ├── MPFoldTransition.m
│ └── MPFlipTransition.m
├── AppDelegate.h
├── MPFoldTransition-Prefix.pch
├── main.m
├── DetailsViewController.h
├── AboutViewController.h
├── StyleTable.h
├── ViewController.h
├── AboutViewController.m
├── DetailsViewController.m
├── MPFoldTransition-Info.plist
├── AppDelegate.m
├── StyleTable.m
└── ViewController.m
├── Default-568h@2x.png
├── MPFoldTransition.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── TODO.markdown
├── .gitignore
├── Source Code License.rtf
└── README.markdown
/MPFoldTransition/en.lproj/InfoPlist.strings:
--------------------------------------------------------------------------------
1 | /* Localized versions of Info.plist keys */
2 |
3 |
--------------------------------------------------------------------------------
/Default-568h@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpospese/MPFoldTransition/HEAD/Default-568h@2x.png
--------------------------------------------------------------------------------
/MPFoldTransition/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpospese/MPFoldTransition/HEAD/MPFoldTransition/Icon.png
--------------------------------------------------------------------------------
/MPFoldTransition/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpospese/MPFoldTransition/HEAD/MPFoldTransition/Icon-72.png
--------------------------------------------------------------------------------
/MPFoldTransition/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpospese/MPFoldTransition/HEAD/MPFoldTransition/Icon@2x.png
--------------------------------------------------------------------------------
/MPFoldTransition/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mpospese/MPFoldTransition/HEAD/MPFoldTransition/Icon-72@2x.png
--------------------------------------------------------------------------------
/MPFoldTransition.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TODO.markdown:
--------------------------------------------------------------------------------
1 | TODO
2 | =====================
3 |
4 | * Test code (minus storyboards) under iOS 4.3 with ARC
5 | * Adjustable # of creases? i.e. accordion or map-style folding?
6 | * ~~Add shadowOpacity (maximum) and shadowColor as properties to allow customization~~
7 | * Reverse Perspective Style for Fold
8 | * Spine position styles for Flip
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldModalSegue.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldModalSegue.h
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MPFoldSegue.h"
11 |
12 | @interface MPFoldModalSegue : MPFoldSegue
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldNavPopSegue.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldNavPopSegue.h
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MPFoldSegue.h"
11 |
12 | @interface MPFoldNavPopSegue : MPFoldSegue
13 |
14 | - (void)perform;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldNavPushSegue.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldNavPushSegue.h
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/4/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MPFoldSegue.h"
11 |
12 | @interface MPFoldNavPushSegue : MPFoldSegue
13 |
14 | - (void)perform;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/MPFoldTransition/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 | + (NSString *)storyboardName;
16 |
17 | @end
18 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition-Prefix.pch:
--------------------------------------------------------------------------------
1 | //
2 | // Prefix header for all source files of the 'MPFoldTransition' target in the 'MPFoldTransition' project
3 | //
4 |
5 | #import
6 |
7 | #ifndef __IPHONE_5_0
8 | #warning "This project uses features only available in iOS SDK 5.0 and later."
9 | #endif
10 |
11 | #ifdef __OBJC__
12 | #import
13 | #import
14 | #endif
15 |
--------------------------------------------------------------------------------
/MPFoldTransition/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | #import "AppDelegate.h"
12 |
13 | int main(int argc, char *argv[])
14 | {
15 | @autoreleasepool {
16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldSegue.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldSegue.h
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | // You must subclass this class, it performs no segue on its own
10 |
11 | #import
12 | #import "MPFoldEnumerations.h"
13 |
14 | @interface MPFoldSegue : UIStoryboardSegue
15 |
16 | @property (assign, nonatomic) MPFoldStyle style;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/MPFoldTransition/DetailsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // DetailsViewController.h
3 | // MPFoldTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MPFoldEnumerations.h"
11 |
12 | @interface DetailsViewController : UIViewController
13 |
14 | @property (assign, nonatomic, getter = isFold) BOOL fold;
15 | @property (assign, nonatomic) NSUInteger style;
16 |
17 | - (IBAction)popPressed:(id)sender;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldModalSegue.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldModalSegue.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPFoldModalSegue.h"
10 | #import "MPFoldTransition.h"
11 |
12 | @implementation MPFoldModalSegue
13 |
14 | - (void)perform
15 | {
16 | [self.sourceViewController presentViewController:self.destinationViewController foldStyle:[self style] completion:nil];
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/MPFoldTransition/AboutViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // AboutViewController.h
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/23/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol MPModalViewControllerDelegate
12 |
13 | // dimiss the modally presented view controller
14 | - (void)dismiss;
15 |
16 | @end
17 |
18 | @interface AboutViewController : UIViewController
19 |
20 | @property (weak, nonatomic) id modalDelegate;
21 |
22 | - (IBAction)donePressed:(id)sender;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 |
4 | MPFoldTransition.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/xcschememanagement.plist
5 |
6 | MPFoldTransition.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/UserInterfaceState.xcuserstate
7 |
8 | MPFoldTransition.xcodeproj/xcuserdata/mark.xcuserdatad/xcschemes/MPFoldTransition.xcscheme
9 |
10 | MPFoldTransition.xcodeproj/project.xcworkspace/xcuserdata/mark.xcuserdatad/WorkspaceSettings.xcsettings
11 |
12 | MPFoldTransition.xcodeproj/xcuserdata/mark.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist
13 |
14 | MPFoldTransition.xcodeproj/project.xcworkspace/xcshareddata/MPFoldTransition.xccheckout
15 |
--------------------------------------------------------------------------------
/MPFoldTransition/StyleTable.h:
--------------------------------------------------------------------------------
1 | //
2 | // StyleTable.h
3 | // MPTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 5/1/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @protocol StyleDelegate;
12 | @interface StyleTable : UITableViewController
13 |
14 | @property (assign, nonatomic, getter = isFold) BOOL fold;
15 | @property (assign, nonatomic) NSUInteger style;
16 | @property(weak, nonatomic) id styleDelegate;
17 | - (IBAction)donePressed:(id)sender;
18 |
19 | @end
20 |
21 | @protocol StyleDelegate
22 |
23 | - (void)styleDidChange:(NSUInteger)newStyle;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPAnimation.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPAnimation.h
3 | // EnterTheMatrix
4 | //
5 | // Created by Mark Pospesel on 3/10/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface MPAnimation : NSObject
12 |
13 | + (UIImage *)renderImageFromView:(UIView *)view;
14 | + (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame;
15 | + (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame transparentInsets:(UIEdgeInsets)insets;
16 | + (UIImage *)renderImageForAntialiasing:(UIImage *)image withInsets:(UIEdgeInsets)insets;
17 | + (UIImage *)renderImageForAntialiasing:(UIImage *)image;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPTransitionEnumerations.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPTransitionEnumerations.h
3 | // MPFoldTransition (v1.0.1)
4 | //
5 | // Created by Mark Pospesel on 5/14/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #ifndef MPFoldTransition_MPTransitionEnumerations_h
10 | #define MPFoldTransition_MPTransitionEnumerations_h
11 |
12 | // Action to take upon completion of the transition
13 | enum {
14 | MPTransitionActionAddRemove, // add/remove subViews upon completion
15 | MPTransitionActionShowHide, // show/hide subViews upon completion
16 | MPTransitionActionNone // take no action (use when container view controller will handle add/remove)
17 | } typedef MPTransitionAction;
18 |
19 |
20 | #endif
21 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFlipSegue.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFlipSegue.h
3 | // MPTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | // You must subclass this class, it performs no segue on its own
10 |
11 | #import
12 | #import "MPFlipEnumerations.h"
13 |
14 | #pragma mark - superclass
15 |
16 | @interface MPFlipSegue : UIStoryboardSegue
17 |
18 | @property (assign, nonatomic) MPFlipStyle style;
19 |
20 | - (void)perform;
21 |
22 | @end
23 |
24 | #pragma mark - subclasses
25 |
26 | @interface MPFlipModalSegue : MPFlipSegue
27 |
28 | @end
29 |
30 | @interface MPFlipNavPushSegue : MPFlipSegue
31 |
32 | @end
33 |
34 | @interface MPFlipNavPopSegue : MPFlipSegue
35 |
36 | @end
37 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldSegue.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldSegue.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPFoldSegue.h"
10 |
11 | @implementation MPFoldSegue
12 |
13 | @synthesize style = _style;
14 |
15 | - (id)init
16 | {
17 | self = [super init];
18 | if (self)
19 | {
20 | [self doInit];
21 | }
22 |
23 | return self;
24 | }
25 |
26 | - (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination
27 | {
28 | self = [super initWithIdentifier:identifier source:source destination:destination];
29 | if (self)
30 | {
31 | [self doInit];
32 | }
33 |
34 | return self;
35 | }
36 |
37 | - (void)doInit
38 | {
39 | _style = [self defaultStyle];
40 | }
41 |
42 | - (MPFoldStyle)defaultStyle
43 | {
44 | return MPFoldStyleDefault;
45 | }
46 |
47 | @end
48 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldNavPushSegue.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldNavPushSegue.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/4/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPFoldNavPushSegue.h"
10 | #import "MPFoldTransition.h"
11 | @implementation MPFoldNavPushSegue
12 |
13 | - (void)perform
14 | {
15 | UIViewController *src = (UIViewController *) self.sourceViewController;
16 | UIViewController *dest = (UIViewController *) self.destinationViewController;
17 | UINavigationController *navController = src.navigationController;
18 | if (!navController)
19 | [NSException raise:@"Invalid Operation" format:@"MPFoldNavPopSegue can only be performed on a child controller of a UINavigationController."];
20 |
21 | if (![[navController visibleViewController] isEqual:src])
22 | [NSException raise:@"Invalid Operation" format:@"MPFoldNavPopSegue can only be performed from the current visibleViewController of a UINavigationController."];
23 |
24 | [navController pushViewController:dest foldStyle:[self style]];
25 | }
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/MPFoldTransition/ViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // MPTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AboutViewController.h"
11 | #import "MPFoldEnumerations.h"
12 | #import "MPFlipEnumerations.h"
13 | #import "StyleTable.h"
14 |
15 | enum {
16 | MPTransitionModeFold,
17 | MPTransitionModeFlip
18 | } typedef MPTransitionMode;
19 |
20 | @interface ViewController : UIViewController
21 |
22 | @property (assign, nonatomic) MPTransitionMode mode;
23 | @property (assign, nonatomic) NSUInteger style;
24 | @property (assign, nonatomic) MPFoldStyle foldStyle;
25 | @property (assign, nonatomic) MPFlipStyle flipStyle;
26 | @property (readonly, nonatomic) BOOL isFold;
27 |
28 | @property (weak, nonatomic) IBOutlet UIView *contentView;
29 | @property (weak, nonatomic) IBOutlet UISegmentedControl *modeSegment;
30 |
31 | - (IBAction)stepperValueChanged:(id)sender;
32 | - (IBAction)stylePressed:(UIBarButtonItem *)sender;
33 | - (IBAction)infoPressed:(id)sender;
34 | - (IBAction)modeValueChanged:(id)sender;
35 | - (IBAction)detailPressed:(id)sender;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFoldNavPopSegue.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldNavPopSegue.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPFoldNavPopSegue.h"
10 | #import "MPFoldTransition.h"
11 |
12 | @implementation MPFoldNavPopSegue
13 |
14 | - (MPFoldStyle)defaultStyle
15 | {
16 | return MPFoldStyleUnfold;
17 | }
18 |
19 | - (void)perform
20 | {
21 | UIViewController *src = (UIViewController *) self.sourceViewController;
22 | UINavigationController *navController = src.navigationController;
23 | if (!navController)
24 | [NSException raise:@"Invalid Operation" format:@"MPFoldNavPopSegue can only be performed on a child controller of a UINavigationController."];
25 | if (![[navController visibleViewController] isEqual:src])
26 | [NSException raise:@"Invalid Operation" format:@"MPFoldNavPopSegue can only be performed from the current visibleViewController of a UINavigationController."];
27 | if ([navController.viewControllers count] < 2)
28 | [NSException raise:@"Invalid Operation" format:@"UINavigationController parent must have at least 2 child controllers in its stack. Otherwise no pop can be performed."];
29 |
30 | [navController popViewControllerWithFoldStyle:[self style]];
31 | }
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/MPFoldTransition/AboutViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // AboutViewController.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/23/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "AboutViewController.h"
10 |
11 | @interface AboutViewController ()
12 |
13 | @end
14 |
15 | @implementation AboutViewController
16 |
17 | @synthesize modalDelegate;
18 |
19 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
20 | {
21 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
22 | if (self) {
23 | // Custom initialization
24 | }
25 | return self;
26 | }
27 |
28 | - (void)viewDidLoad
29 | {
30 | [super viewDidLoad];
31 | // Do any additional setup after loading the view.
32 | }
33 |
34 | - (void)viewDidUnload
35 | {
36 | [super viewDidUnload];
37 | // Release any retained subviews of the main view.
38 | }
39 |
40 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
41 | {
42 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
43 | return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
44 | } else {
45 | return YES;
46 | }
47 | }
48 |
49 | - (IBAction)donePressed:(id)sender {
50 | // notify delegate that we were dismissed
51 | [[self modalDelegate] dismiss];
52 | }
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPFoldEnumerations.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldEnumerations.h
3 | // MPFoldTransition (v1.0.1)
4 | //
5 | // Created by Mark Pospesel on 4/26/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #ifndef MPFoldTransition_MPFoldEnumerations_h
10 | #define MPFoldTransition_MPFoldEnumerations_h
11 |
12 | // Bit 0: Fold vs. Unfold
13 | // Fold = view disappears by folding away from user into center to nothing
14 | // Unfold = view appears from nothing by unfolding from center
15 |
16 | // Bit 1: Normal vs. Cubic
17 | // Normal = Top & bottom (or left & right for Horizontal) sleeves are flat and slide into/out of view as center view folds/unfolds
18 | // Cubic = Top & bottom (or left & right for Horizontal) sleeves are fixed at 90 degree angles to the central folding panels,
19 | // so that they unfold/fold as center view folds/unfolds.
20 |
21 | // Bit 2: Vertical vs. Horizontal
22 | // Vertical = view folds/unfolds into top & bottom halves
23 | // Horizontal = view folds/unfolds into left & right halves
24 |
25 | enum {
26 | // current view folds away into center, next view slides in flat from top & bottom
27 | MPFoldStyleDefault = 0,
28 | MPFoldStyleUnfold = 1 << 0,
29 | MPFoldStyleCubic = 1 << 1,
30 | MPFoldStyleHorizontal = 1 << 2
31 | };
32 | typedef NSUInteger MPFoldStyle;
33 |
34 | static inline MPFoldStyle MPFoldStyleFlipFoldBit(MPFoldStyle style) { return (style & ~MPFoldStyleUnfold) | ((style & MPFoldStyleUnfold) == MPFoldStyleUnfold? 0 : MPFoldStyleUnfold); }
35 |
36 | #endif
37 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPFlipEnumerations.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFlipEnumerations.h
3 | // MPTransition (v1.1.0)
4 | //
5 | // Created by Mark Pospesel on 5/15/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #ifndef MPFoldTransition_MPFlipEnumerations_h
10 | #define MPFoldTransition_MPFlipEnumerations_h
11 |
12 | // Bit 0: Direction - Forward (unset) vs.Backward (set)
13 | // Forward = page flip from right to left (horizontal) or bottom to top (vertical)
14 | // Backward = page flip from left to right (horizontal) or top to bottom (vertical)
15 |
16 | // Bit 1: Orientation - Horizontal (unset) vs. Vertical (set)
17 | // Horizontal = page flips right to left about a vertical spine
18 | // Vertical = page flips bottom to top about a horizontal spine
19 |
20 | // Bit 2: Perspective - Normal (unset) vs. Reverse (set)
21 | // Normal = page flips towards viewer
22 | // Reverse = page flips away from viewer
23 |
24 | // TODO: spine position (left, mid, right // top, mid, bottom)
25 |
26 | enum {
27 | // current view folds away into center, next view slides in flat from top & bottom
28 | MPFlipStyleDefault = 0,
29 | MPFlipStyleDirectionBackward = 1 << 0,
30 | MPFlipStyleOrientationVertical = 1 << 1,
31 | MPFlipStylePerspectiveReverse = 1 << 2
32 | };
33 | typedef NSUInteger MPFlipStyle;
34 |
35 | #define MPFlipStyleDirectionMask MPFlipStyleDirectionBackward
36 | #define MPFlipStyleOrientationMask MPFlipStyleOrientationVertical
37 | #define MPFlipStylePerspectiveMask MPFlipStylePerspectiveReverse
38 |
39 | static inline MPFlipStyle MPFlipStyleFlipDirectionBit(MPFlipStyle style) { return (style & ~MPFlipStyleDirectionMask) | ((style & MPFlipStyleDirectionMask) == MPFlipStyleDirectionBackward? 0 : MPFlipStyleDirectionBackward); }
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPTransition.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPTransition.h
3 | // MPFoldTransition (v1.1.4)
4 | //
5 | // Created by Mark Pospesel on 5/14/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MPTransitionEnumerations.h"
11 |
12 | typedef void (^CompletionBlock)(BOOL);
13 |
14 |
15 | @interface MPTransition : NSObject
16 |
17 | #pragma mark - Properties
18 |
19 | @property (strong, nonatomic) UIView *sourceView;
20 | @property (strong, nonatomic) UIView *destinationView;
21 | @property (assign, nonatomic) NSTimeInterval duration;
22 | @property (assign, nonatomic) CGRect rect;
23 | @property (assign, nonatomic) MPTransitionAction completionAction;
24 | @property (assign, nonatomic) UIViewAnimationCurve timingCurve;
25 | @property (readonly, nonatomic) BOOL presentedControllerIncludesStatusBarInFrame;
26 |
27 | // Perspective component of transformation (Advanced use only, generally don't need to adjust)
28 | @property (assign, nonatomic) float m34;
29 |
30 | // Special case of dismissing a modal view
31 | @property (assign, nonatomic, getter = isDimissing) BOOL dismissing;
32 |
33 | #pragma mark - init
34 |
35 | - (id)initWithSourceView:(UIView *)sourceView destinationView:(UIView *)destinationView duration:(NSTimeInterval)duration timingCurve:(UIViewAnimationCurve)timingCurve completionAction:(MPTransitionAction)action;
36 |
37 | #pragma mark - Instance methods
38 |
39 | - (void)perform;
40 | - (void)perform:(void (^)(BOOL finished))completion;
41 |
42 | - (NSString *)timingCurveFunctionName;
43 | - (void)transitionDidComplete;
44 | - (void)setPresentingController:(UIViewController *)presentingController;
45 | - (void)setPresentedController:(UIViewController *)presentedController;
46 | - (CGRect)calculateRect;
47 |
48 | #pragma mark - Class methods
49 |
50 | + (NSTimeInterval)defaultDuration;
51 |
52 | @end
53 |
54 | #pragma mark - UIView extensions
55 |
56 | @interface UIView(MPAnimation)
57 |
58 | + (BOOL)subView:(UIView *)subView1 isAboveSubView:(UIView *)subView2;
59 | + (BOOL)subView:(UIView *)subView1 isBelowSubView:(UIView *)subView2;
60 | - (BOOL)isAboveSiblingView:(UIView *)siblingView;
61 | - (BOOL)isBelowSiblingView:(UIView *)siblingView;
62 |
63 | @end
--------------------------------------------------------------------------------
/MPFoldTransition/DetailsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // DetailsViewController.m
3 | // MPFoldTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "DetailsViewController.h"
10 | #import "MPFoldSegue.h"
11 | #import "MPFoldTransition.h"
12 | #import "MPFlipTransition.h"
13 |
14 | @interface DetailsViewController ()
15 |
16 | @end
17 |
18 | @implementation DetailsViewController
19 |
20 | @synthesize style = _style;
21 | @synthesize fold = _fold;
22 |
23 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
24 | {
25 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
26 | if (self) {
27 | // Custom initialization
28 | }
29 | return self;
30 | }
31 |
32 | - (void)viewDidLoad
33 | {
34 | [super viewDidLoad];
35 | // Do any additional setup after loading the view.
36 | [self.navigationItem setHidesBackButton:YES animated:NO];
37 | }
38 |
39 | - (void)viewDidUnload
40 | {
41 | [super viewDidUnload];
42 | // Release any retained subviews of the main view.
43 | }
44 |
45 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
46 | {
47 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
48 | return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
49 | } else {
50 | return YES;
51 | }
52 | }
53 |
54 | // I removed the segues from the storyboards so that I could switch between flip and fold segue classes
55 | /*- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
56 | {
57 | if ([segue isKindOfClass:[MPFoldSegue class]])
58 | {
59 | MPFoldSegue *foldSegue = (MPFoldSegue *)segue;
60 | // do the opposite fold style from the transition that presented this view
61 | [foldSegue setStyle:MPFoldStyleFlipFoldBit([self style])];
62 | }
63 | }*/
64 |
65 | - (IBAction)popPressed:(id)sender {
66 | // do the opposite fold/flip style from the transition that presented this view
67 | if ([self isFold])
68 | [self.navigationController popViewControllerWithFoldStyle:MPFoldStyleFlipFoldBit([self style])];
69 | else
70 | [self.navigationController popViewControllerWithFlipStyle:MPFlipStyleFlipDirectionBit([self style])];
71 | }
72 |
73 | @end
74 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | MPTransition
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleIcons
12 |
13 | CFBundlePrimaryIcon
14 |
15 | CFBundleIconFiles
16 |
17 | Icon.png
18 | Icon@2x.png
19 | Icon-72.png
20 | Icon-72@2x.png
21 |
22 | UIPrerenderedIcon
23 |
24 |
25 |
26 | CFBundleIdentifier
27 | com.markpospesel.${PRODUCT_NAME:rfc1034identifier}
28 | CFBundleInfoDictionaryVersion
29 | 6.0
30 | CFBundleName
31 | ${PRODUCT_NAME}
32 | CFBundlePackageType
33 | APPL
34 | CFBundleShortVersionString
35 | 1.1
36 | CFBundleSignature
37 | ????
38 | CFBundleVersion
39 | 1.1.4
40 | LSRequiresIPhoneOS
41 |
42 | UIMainStoryboardFile
43 | MainStoryboard_iPhone
44 | UIMainStoryboardFile~ipad
45 | MainStoryboard_iPad
46 | UIPrerenderedIcon
47 |
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UIStatusBarHidden
53 |
54 | UIStatusBarStyle
55 | UIStatusBarStyleDefault
56 | UISupportedInterfaceOrientations
57 |
58 | UIInterfaceOrientationPortrait
59 | UIInterfaceOrientationLandscapeLeft
60 | UIInterfaceOrientationLandscapeRight
61 |
62 | UISupportedInterfaceOrientations~ipad
63 |
64 | UIInterfaceOrientationPortrait
65 | UIInterfaceOrientationPortraitUpsideDown
66 | UIInterfaceOrientationLandscapeLeft
67 | UIInterfaceOrientationLandscapeRight
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/MPFoldTransition/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // MPFoldTransition (v 1.0.0)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 |
11 | #define IPHONE_STORYBOARD_NAME @"MainStoryboard_iPhone";
12 | #define IPAD_STORYBOARD_NAME @"MainStoryboard_iPad";
13 |
14 | @implementation AppDelegate
15 |
16 | @synthesize window = _window;
17 |
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
19 | {
20 | //[[self window] setBackgroundColor:[UIColor yellowColor]];
21 |
22 | // Override point for customization after application launch.
23 | return YES;
24 | }
25 |
26 | - (void)applicationWillResignActive:(UIApplication *)application
27 | {
28 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
29 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
30 | }
31 |
32 | - (void)applicationDidEnterBackground:(UIApplication *)application
33 | {
34 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
35 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
36 | }
37 |
38 | - (void)applicationWillEnterForeground:(UIApplication *)application
39 | {
40 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
41 | }
42 |
43 | - (void)applicationDidBecomeActive:(UIApplication *)application
44 | {
45 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
46 | }
47 |
48 | - (void)applicationWillTerminate:(UIApplication *)application
49 | {
50 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
51 | }
52 |
53 | + (NSString *)storyboardName
54 | {
55 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
56 | return IPHONE_STORYBOARD_NAME;
57 | } else {
58 | return IPAD_STORYBOARD_NAME;
59 | }
60 | }
61 |
62 | @end
63 |
--------------------------------------------------------------------------------
/Source Code License.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320
2 | {\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\fmodern\fcharset0 Courier-Bold;\f2\fmodern\fcharset0 Courier;
3 | }
4 | {\colortbl;\red255\green255\blue255;\red247\green247\blue247;\red103\green103\blue103;\red77\green77\blue77;
5 | }
6 | \margl1440\margr1440\vieww21460\viewh13140\viewkind0
7 | \deftab720
8 | \pard\pardeftab720
9 |
10 | \f0\b\fs24 \cf0 \cb2 MPTransition\
11 | Mark Pospesel, Source Code License
12 | \b0 \
13 |
14 | \fs20 \cf3 Last updated: April 19, 2012
15 | \fs24 \cf0 \
16 | \
17 | The software license is based on the 3-clause "New BSD License", with an attribution requirement added. Basically you can use this code, original or modified, in your own products, including commercial and/or closed-source products. The only 2 things I ask are:\
18 | \
19 | 1. Include the full
20 | \f1\b\fs26 \cf4 License Agreement
21 | \f0\b0\fs24 \cf0 text below.\
22 | 2. Include a credit mentioning me as the original author of the source (e.g.
23 | \b Includes MPTransition by Mark Pospesel
24 | \b0 ). I would prefer that this credit be in the software's "About" window, but it could also be included in the list of Acknowledgments, in the software's documentation, or on the software's website.\
25 | \
26 | If you need an attribution-free version of this license for use in a product, please contact me via one of the methods listed under {\field{\*\fldinst{HYPERLINK "http://markpospesel.com/about"}}{\fldrslt http://markpospesel.com/about}}
27 | \fs26 \
28 | \
29 |
30 | \f2 \
31 |
32 | \f1\b \cf4 License Agreement
33 | \f2\b0 \
34 | \
35 | Copyright (c) 2012, Mark Pospesel\
36 | All rights reserved.\
37 | \
38 | Redistribution and use in source and binary forms, with or without\
39 | modification, are permitted provided that the following conditions are met:\
40 | * Redistributions of source code must retain the above copyright\
41 | notice, this list of conditions, attribution of Mark Pospesel as \
42 | the original author of the source code and the following disclaimer.\
43 | * Redistributions in binary form must reproduce the above copyright\
44 | notice, this list of conditions, attribution of Mark Pospesel as \
45 | the original author of the source code and the following disclaimer in the\
46 | documentation and/or other materials provided with the distribution.\
47 | * The name of Mark Pospesel may not be used to endorse or promote products\
48 | derived from this software without specific prior written permission.\
49 | \
50 | THIS SOFTWARE IS PROVIDED BY MARK POSPESEL "AS IS" AND\
51 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\
52 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\
53 | DISCLAIMED. IN NO EVENT SHALL MARK POSPESEL BE LIABLE FOR ANY\
54 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\
55 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\
56 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\
57 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\
58 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\
59 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.}
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/Segues/MPFlipSegue.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFlipSegue.m
3 | // MPTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 4/18/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPFlipSegue.h"
10 | #import "MPFlipTransition.h"
11 |
12 | #pragma mark - superclass
13 |
14 | @implementation MPFlipSegue
15 |
16 | @synthesize style = _style;
17 |
18 | - (id)init
19 | {
20 | self = [super init];
21 | if (self)
22 | {
23 | [self doInit];
24 | }
25 |
26 | return self;
27 | }
28 |
29 | - (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(UIViewController *)destination
30 | {
31 | self = [super initWithIdentifier:identifier source:source destination:destination];
32 | if (self)
33 | {
34 | [self doInit];
35 | }
36 |
37 | return self;
38 | }
39 |
40 | - (void)doInit
41 | {
42 | _style = [self defaultStyle];
43 | }
44 |
45 | - (MPFlipStyle)defaultStyle
46 | {
47 | return MPFlipStyleDefault;
48 | }
49 |
50 | - (void)perform
51 | {
52 | [NSException raise:@"Incomplete Implementation" format:@"MPFlipSegue must be subclassed and the perform method implemented."];
53 | }
54 |
55 | @end
56 |
57 | #pragma mark - subclasses
58 |
59 | @implementation MPFlipModalSegue
60 |
61 | - (void)perform
62 | {
63 | [self.sourceViewController presentViewController:self.destinationViewController flipStyle:[self style] completion:nil];
64 | }
65 |
66 | @end
67 |
68 | @implementation MPFlipNavPushSegue
69 |
70 | - (void)perform
71 | {
72 | UIViewController *src = (UIViewController *) self.sourceViewController;
73 | UIViewController *dest = (UIViewController *) self.destinationViewController;
74 | UINavigationController *navController = src.navigationController;
75 | if (!navController)
76 | [NSException raise:@"Invalid Operation" format:@"MPFlipNavPopSegue can only be performed on a child controller of a UINavigationController."];
77 |
78 | if (![[navController visibleViewController] isEqual:src])
79 | [NSException raise:@"Invalid Operation" format:@"MPFlipNavPopSegue can only be performed from the current visibleViewController of a UINavigationController."];
80 |
81 | [navController pushViewController:dest flipStyle:[self style]];
82 | }
83 |
84 | @end
85 |
86 | @implementation MPFlipNavPopSegue
87 |
88 | - (MPFlipStyle)defaultStyle
89 | {
90 | return MPFlipStyleDirectionBackward;
91 | }
92 |
93 | - (void)perform
94 | {
95 | UIViewController *src = (UIViewController *) self.sourceViewController;
96 | UINavigationController *navController = src.navigationController;
97 | if (!navController)
98 | [NSException raise:@"Invalid Operation" format:@"MPFlipNavPopSegue can only be performed on a child controller of a UINavigationController."];
99 | if (![[navController visibleViewController] isEqual:src])
100 | [NSException raise:@"Invalid Operation" format:@"MPFlipNavPopSegue can only be performed from the current visibleViewController of a UINavigationController."];
101 | if ([navController.viewControllers count] < 2)
102 | [NSException raise:@"Invalid Operation" format:@"UINavigationController parent must have at least 2 child controllers in its stack. Otherwise no pop can be performed."];
103 |
104 | [navController popViewControllerWithFlipStyle:[self style]];
105 | }
106 |
107 | @end
108 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPFoldTransition.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldTransition.h
3 | // MPFoldTransition (v1.0.1)
4 | //
5 | // Created by Mark Pospesel on 4/4/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "MPFoldEnumerations.h"
11 | #import "MPTransition.h"
12 |
13 | @interface MPFoldTransition : MPTransition
14 |
15 | #pragma mark - Properties
16 |
17 | // Fold style
18 | @property (assign, nonatomic) MPFoldStyle style;
19 |
20 | // Maximum shadow opacity (when fully folded), default = 0.25
21 | @property (assign, nonatomic) CGFloat foldShadowOpacity;
22 |
23 | // Fold shadow color, default = black
24 | @property (strong, nonatomic) UIColor *foldShadowColor;
25 |
26 | #pragma mark - init methods
27 |
28 | - (id)initWithSourceView:(UIView *)sourceView destinationView:(UIView *)destinationView duration:(NSTimeInterval)duration style:(MPFoldStyle)style completionAction:(MPTransitionAction)action;
29 |
30 | #pragma mark - Instance methods
31 |
32 | - (void)perform:(void (^)(BOOL finished))completion;
33 |
34 | #pragma mark - Class methods
35 |
36 | // For generic UIViewController transitions
37 | + (void)transitionFromViewController:(UIViewController *)fromController
38 | toViewController:(UIViewController *)toController
39 | duration:(NSTimeInterval)duration
40 | style:(MPFoldStyle)style
41 | completion:(void (^)(BOOL finished))completion;
42 |
43 | // For generic UIView transitions
44 | + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration style:(MPFoldStyle)style transitionAction:(MPTransitionAction)action completion:(void (^)(BOOL finished))completion;
45 |
46 | // To present a view controller modally
47 | + (void)presentViewController:(UIViewController *)viewControllerToPresent from:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion;
48 |
49 | // To dismiss a modal view controller
50 | + (void)dismissViewControllerFromPresentingController:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion;
51 |
52 | @end
53 |
54 | #pragma mark - UIViewController extensions
55 |
56 | // Convenience method extensions for UIViewController
57 | @interface UIViewController(MPFoldTransition)
58 |
59 | // present view controller modally with fold transition
60 | // use like presentViewController:animated:completion:
61 | - (void)presentViewController:(UIViewController *)viewControllerToPresent foldStyle:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion;
62 |
63 | // dismiss presented controller with fold transition
64 | // use like dismissViewControllerAnimated:completion:
65 | - (void)dismissViewControllerWithFoldStyle:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion;
66 |
67 | @end
68 |
69 | #pragma mark - UINavigationController extensions
70 |
71 | // Convenience method extensions for UINavigationController
72 | @interface UINavigationController(MPFoldTransition)
73 |
74 | //- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
75 | - (void)pushViewController:(UIViewController *)viewController foldStyle:(MPFoldStyle)style;
76 |
77 | //- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
78 | - (UIViewController *)popViewControllerWithFoldStyle:(MPFoldStyle)style;
79 |
80 | //- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;
81 | - (NSArray*)popToRootViewControllerWithFoldStyle:(MPFoldStyle)style;
82 |
83 | @end
84 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPFlipTransition.h:
--------------------------------------------------------------------------------
1 | //
2 | // MPFlipTransition.h
3 | // MPTransition (v1.1.0)
4 | //
5 | // Created by Mark Pospesel on 5/15/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPFlipEnumerations.h"
10 | #import "MPTransition.h"
11 |
12 | @interface MPFlipTransition : MPTransition
13 |
14 | #pragma mark - Properties
15 |
16 | @property (assign, nonatomic) MPFlipStyle style;
17 | @property (assign, nonatomic) CGFloat coveredPageShadowOpacity;
18 | @property (assign, nonatomic) CGFloat flippingPageShadowOpacity;
19 | @property (strong, nonatomic) UIColor *flipShadowColor;
20 |
21 | #pragma mark - init
22 |
23 | - (id)initWithSourceView:(UIView *)sourceView destinationView:(UIView *)destinationView duration:(NSTimeInterval)duration style:(MPFlipStyle)style completionAction:(MPTransitionAction)action;
24 |
25 | #pragma mark - Instance methods
26 |
27 | // builds the layers for the flip animation
28 | - (void)buildLayers;
29 |
30 | // performs the flip animation
31 | - (void)perform:(void (^)(BOOL finished))completion;
32 |
33 | // set view to any position within the 1st half of the animation
34 | // progress ranges from 0 (start) to 1 (complete)
35 | - (void)doFlip1:(CGFloat)progress;
36 |
37 | // set view to any position within the 2nd half of the animation
38 | // progress ranges from 0 (start) to 1 (complete)
39 | - (void)doFlip2:(CGFloat)progress;
40 |
41 | #pragma mark - Class methods
42 |
43 | // For generic UIViewController transitions
44 | + (void)transitionFromViewController:(UIViewController *)fromController
45 | toViewController:(UIViewController *)toController
46 | duration:(NSTimeInterval)duration
47 | style:(MPFlipStyle)style
48 | completion:(void (^)(BOOL finished))completion;
49 |
50 | // For generic UIView transitions
51 | + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration style:(MPFlipStyle)style transitionAction:(MPTransitionAction)action completion:(void (^)(BOOL finished))completion;
52 |
53 | // To present a view controller modally
54 | + (void)presentViewController:(UIViewController *)viewControllerToPresent from:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion;
55 |
56 | // To dismiss a modal view controller
57 | + (void)dismissViewControllerFromPresentingController:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion;
58 |
59 | @end
60 |
61 | #pragma mark - UIViewController extensions
62 |
63 | // Convenience method extensions for UIViewController
64 | @interface UIViewController(MPFlipTransition)
65 |
66 | // present view controller modally with fold transition
67 | // use like presentViewController:animated:completion:
68 | - (void)presentViewController:(UIViewController *)viewControllerToPresent flipStyle:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion;
69 |
70 | // dismiss presented controller with fold transition
71 | // use like dismissViewControllerAnimated:completion:
72 | - (void)dismissViewControllerWithFlipStyle:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion;
73 |
74 | @end
75 |
76 | #pragma mark - UINavigationController extensions
77 |
78 | // Convenience method extensions for UINavigationController
79 | @interface UINavigationController(MPFlipTransition)
80 |
81 | //- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
82 | - (void)pushViewController:(UIViewController *)viewController flipStyle:(MPFlipStyle)style;
83 |
84 | //- (UIViewController *)popViewControllerAnimated:(BOOL)animated;
85 | - (UIViewController *)popViewControllerWithFlipStyle:(MPFlipStyle)style;
86 |
87 | //- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated;
88 | - (NSArray*)popToRootViewControllerWithFlipStyle:(MPFlipStyle)style;
89 |
90 | @end
91 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPAnimation.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPAnimation.m
3 | // EnterTheMatrix
4 | //
5 | // Created by Mark Pospesel on 3/10/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "MPAnimation.h"
10 | #import
11 |
12 | @implementation MPAnimation
13 |
14 | // Generates an image from the view (view must be opaque)
15 | + (UIImage *)renderImageFromView:(UIView *)view
16 | {
17 | return [self renderImageFromView:view withRect:view.bounds];
18 | }
19 |
20 | // Generates an image from the (opaque) view where frame is a rectangle in the view's coordinate space.
21 | // Pass in bounds to render the entire view, or another rect to render a subset of the view
22 | + (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame
23 | {
24 | // Create a new context of the desired size to render the image
25 | UIGraphicsBeginImageContextWithOptions(frame.size, YES, 0);
26 | CGContextRef context = UIGraphicsGetCurrentContext();
27 |
28 | // Translate it, to the desired position
29 | CGContextTranslateCTM(context, -frame.origin.x, -frame.origin.y);
30 |
31 | // Render the view as image
32 | [view.layer renderInContext:context];
33 |
34 | // Fetch the image
35 | UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
36 |
37 | // Cleanup
38 | UIGraphicsEndImageContext();
39 |
40 | return renderedImage;
41 | }
42 |
43 | // Generates an image from the view with transparent margins.
44 | // (CGRect)frame is a rectangle in the view's coordinate space- pass in bounds to render the entire view, or another rect to render a subset of the view
45 | // (UIEdgeInsets)insets defines the size of the transparent margins to create
46 | + (UIImage *)renderImageFromView:(UIView *)view withRect:(CGRect)frame transparentInsets:(UIEdgeInsets)insets
47 | {
48 | CGSize imageSizeWithBorder = CGSizeMake(frame.size.width + insets.left + insets.right, frame.size.height + insets.top + insets.bottom);
49 | // Create a new context of the desired size to render the image
50 | UIGraphicsBeginImageContextWithOptions(imageSizeWithBorder, UIEdgeInsetsEqualToEdgeInsets(insets, UIEdgeInsetsZero), 0);
51 | CGContextRef context = UIGraphicsGetCurrentContext();
52 |
53 | // Clip the context to the portion of the view we will draw
54 | CGContextClipToRect(context, (CGRect){{insets.left, insets.top}, frame.size});
55 | // Translate it, to the desired position
56 | CGContextTranslateCTM(context, -frame.origin.x + insets.left, -frame.origin.y + insets.top);
57 |
58 | // Render the view as image
59 | [view.layer renderInContext:context];
60 |
61 | // Fetch the image
62 | UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
63 |
64 | // Cleanup
65 | UIGraphicsEndImageContext();
66 |
67 | return renderedImage;
68 | }
69 |
70 | // Generates a copy of the image with a 1 point transparent margin around it
71 | + (UIImage *)renderImageForAntialiasing:(UIImage *)image
72 | {
73 | return [self renderImageForAntialiasing:image withInsets:UIEdgeInsetsMake(1, 1, 1, 1)];
74 | }
75 |
76 | // Generates a copy of the image with transparent margins of size defined by the insets parameter
77 | + (UIImage *)renderImageForAntialiasing:(UIImage *)image withInsets:(UIEdgeInsets)insets
78 | {
79 | CGSize imageSizeWithBorder = CGSizeMake([image size].width + insets.left + insets.right, [image size].height + insets.top + insets.bottom);
80 |
81 | // Create a new context of the desired size to render the image
82 | UIGraphicsBeginImageContextWithOptions(imageSizeWithBorder, UIEdgeInsetsEqualToEdgeInsets(insets, UIEdgeInsetsZero), 0);
83 |
84 | // The image starts off filled with clear pixels, so we don't need to explicitly fill them here
85 | [image drawInRect:(CGRect){{insets.left, insets.top}, [image size]}];
86 |
87 | // Fetch the image
88 | UIImage *renderedImage = UIGraphicsGetImageFromCurrentImageContext();
89 |
90 | UIGraphicsEndImageContext();
91 |
92 | return renderedImage;
93 | }
94 |
95 |
96 | @end
97 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | MPTransition
2 | =====================
3 | __Update__: MPTransition now comprises MPFoldTransition and MPFlipTransition
4 |
5 | __Update 2__: For a touch gesture-enabled container controller with page-flipping (not just a transition), see [MPFlipViewController](https://github.com/mpospese/MPFlipViewController)
6 |
7 | MPFoldTransition is a set of classes to add folding-style transitions to iOS 5 projects.
8 | 
9 | MPFlipTransition is a set of classes to add page-flipping transitions to iOS 5 projects.
10 | 
11 | I built it using ARC (and for the demo portion storyboards) strictly for convenience, so it uses iOS 5. I imagine the relevant code (minus the UIStoryboardSegue helper classes) could be easily ported to iOS 4.3 under ARC, or ported to iOS 4.0 with memory management inserted. (_Hint:_ If you're porting to non-ARC, you'll need to retain [sourceView superView] in the init method.)
12 |
13 | Features
14 | ---------
15 | * Convenience methods to extend UIViewController to present/dismiss a view controller modally using fold/flip transitions
16 | * Convenience methods to extend UINavigationController to push/pop view controllers onto the navigation stack using fold/flip transitions
17 | * Convenience methods to transition between any 2 UIViewControllers or UIViews
18 | * 3 Custom UIStoryboardSegue subclasses to easily add folding/flipping transitions via Interface Builder in your storyboards
19 | * Fully customizable to adjust style, duration, timing curves, and completion action
20 | * Blocks-based: many methods include a completion block parameter following the pattern of block-based animations introduced in iOS 4.
21 |
22 | Fold Styles
23 | ---------
24 | Currently there are 3 style bits that can be combined for 8 different styles.
25 | * __Direction:__ Fold vs. Unfold
26 | 
27 | * __Mode:__ Normal vs. Cubic
28 | 
29 | * __Orientation:__ Vertical vs. Horizontal
30 | 
31 |
32 | Flip Styles
33 | ---------
34 | Currently there are 3 style bits that can be combined for 8 different styles.
35 | * __Direction:__ Forward vs. Backward
36 | 
37 | * __Orientation:__ Horizontal vs. Vertical
38 | 
39 | * __Perspective:__ Normal vs. Reverse
40 | 
41 |
42 | Requirements
43 | -----------
44 | * Xcode 4.4 or higher
45 | * LLVM compiler
46 | * iOS 5 or higher
47 |
48 | How To Use
49 | ---------
50 | See the "MPFoldTransition.h" (and "MPFlipTransition.h") header file(s) for methods and use the demo project as a reference. The Segue classes (under directory of the same name) are optional, only if you want to include storyboard support. Otherwise you just need the classes under the bottommost MPFoldTransition directory.
51 |
52 | Licensing
53 | ---------
54 | Read Source Code License.rtf, but the gist is:
55 | * Anyone can use it for any type of project
56 | * All I ask for is attribution somewhere
57 |
58 | Support, bugs and feature requests
59 | ----------------------------------
60 | There is absolutely no support offered with this component. You're on your own! If you want to submit a feature request, please do so via [the issue tracker on github](https://github.com/mpospese/MPFoldTransition/issues).
61 |
62 | If you want to submit a bug report, please also do so via the issue tracker, including a diagnosis of the problem and a suggested fix (in code). If you're using MPTransition, you're a developer - so I expect you to do your homework and provide a fix along with each bug report. You can also submit pull requests or patches.
63 |
64 | Please don't submit bug reports without fixes!
65 |
66 | (The preceding blurb provided courtesy of the legendary [Matt Gemmell](https://github.com/mattgemmell/))
67 |
68 | Best,
69 | Mark Pospesel
70 |
71 | Website: http://markpospesel.com/
72 | Contact: http://markpospesel.com/about
73 | Twitter: http://twitter.com/mpospese
74 | Hire Me: http://crazymilksoftware.com/
75 |
--------------------------------------------------------------------------------
/MPFoldTransition/StyleTable.m:
--------------------------------------------------------------------------------
1 | //
2 | // StyleTable.m
3 | // MPTransition (v 1.1.0)
4 | //
5 | // Created by Mark Pospesel on 5/1/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "StyleTable.h"
10 | #import "MPFoldTransition.h"
11 | #import "MPFlipTransition.h"
12 |
13 | @interface StyleTable ()
14 |
15 | @end
16 |
17 | @implementation StyleTable
18 |
19 | @synthesize fold=_fold;
20 | @synthesize style=_style;
21 | @synthesize styleDelegate=_styleDelegate;
22 |
23 | - (id)initWithStyle:(UITableViewStyle)style
24 | {
25 | self = [super initWithStyle:style];
26 | if (self) {
27 | // Custom initialization
28 | }
29 | return self;
30 | }
31 |
32 | - (void)viewDidLoad
33 | {
34 | [super viewDidLoad];
35 |
36 | self.navigationItem.hidesBackButton = YES;
37 |
38 | // Uncomment the following line to preserve selection between presentations.
39 | // self.clearsSelectionOnViewWillAppear = NO;
40 |
41 | // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
42 | // self.navigationItem.rightBarButtonItem = self.editButtonItem;
43 | }
44 |
45 | - (void)viewDidUnload
46 | {
47 | [super viewDidUnload];
48 | // Release any retained subviews of the main view.
49 | // e.g. self.myOutlet = nil;
50 | }
51 |
52 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
53 | {
54 | return YES;
55 | }
56 |
57 | - (CGSize)contentSizeForViewInPopover
58 | {
59 | return CGSizeMake(240, (6 * [self.tableView rowHeight]) + (3 * [self.tableView sectionHeaderHeight]));
60 | }
61 |
62 | #pragma mark - Table view data source
63 |
64 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
65 | {
66 | // row = whether bit flag is on/off
67 | int row = [indexPath row];
68 | // section = which bit flag
69 | int section = [indexPath section];
70 | if (row == (([self style] & (1 << section)) >> section))
71 | cell.accessoryType = UITableViewCellAccessoryCheckmark;
72 | else
73 | cell.accessoryType = UITableViewCellAccessoryNone;
74 | }
75 |
76 | /*
77 | // Override to support conditional editing of the table view.
78 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
79 | {
80 | // Return NO if you do not want the specified item to be editable.
81 | return YES;
82 | }
83 | */
84 |
85 | /*
86 | // Override to support editing the table view.
87 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
88 | {
89 | if (editingStyle == UITableViewCellEditingStyleDelete) {
90 | // Delete the row from the data source
91 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
92 | }
93 | else if (editingStyle == UITableViewCellEditingStyleInsert) {
94 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
95 | }
96 | }
97 | */
98 |
99 | /*
100 | // Override to support rearranging the table view.
101 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
102 | {
103 | }
104 | */
105 |
106 | /*
107 | // Override to support conditional rearranging of the table view.
108 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
109 | {
110 | // Return NO if you do not want the item to be re-orderable.
111 | return YES;
112 | }
113 | */
114 |
115 | #pragma mark - Table view delegate
116 |
117 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
118 | {
119 | // row = whether bit flag is on/off
120 | int row = [indexPath row];
121 | // section = which bit flag
122 | int section = [indexPath section];
123 | if (row != (([self style] & (1 << section)) >> section))
124 | {
125 | UITableViewCell *oldCell = [tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:(([self style] & (1 << section)) >> section) inSection:section]];
126 | [oldCell setAccessoryType:UITableViewCellAccessoryNone];
127 |
128 | UITableViewCell *newCell = [tableView cellForRowAtIndexPath:indexPath];
129 | [newCell setAccessoryType:UITableViewCellAccessoryCheckmark];
130 | }
131 |
132 | NSUInteger newStyle = ([self style] & ~(1 << section)) | (row << section);
133 | [self setStyle:newStyle];
134 | [[self styleDelegate] styleDidChange:self.style];
135 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
136 | }
137 |
138 | - (IBAction)donePressed:(id)sender {
139 | if ([self isFold])
140 | {
141 | // use the opposite fold style from the selected style
142 | MPFoldStyle popStyle = MPFoldStyleFlipFoldBit([self style]);
143 |
144 | [self.navigationController popViewControllerWithFoldStyle:popStyle];
145 | }
146 | else
147 | {
148 | // use the opposite flip style from the selected style
149 | MPFlipStyle popStyle = MPFlipStyleFlipDirectionBit([self style]);
150 |
151 | [self.navigationController popViewControllerWithFlipStyle:popStyle];
152 | }
153 | }
154 |
155 | @end
156 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPTransition.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPTransition.m
3 | // MPFoldTransition (v1.1.4)
4 | //
5 | // Created by Mark Pospesel on 5/14/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #define DEFAULT_DURATION 0.3
10 |
11 | #import "MPTransition.h"
12 | #import
13 |
14 | @interface MPTransition ()
15 |
16 | @property (assign, nonatomic) BOOL presentedControllerIncludesStatusBarInFrame;
17 |
18 | @end
19 |
20 | @implementation MPTransition
21 |
22 | @synthesize sourceView = _sourceView;
23 | @synthesize destinationView = _destinationView;
24 | @synthesize duration = _duration;
25 | @synthesize rect = _rect;
26 | @synthesize completionAction = _completionAction;
27 | @synthesize timingCurve = _timingCurve;
28 | @synthesize dismissing = _dismissing;
29 | @synthesize m34 = _m34;
30 | @synthesize presentedControllerIncludesStatusBarInFrame = _presentedControllerIncludesStatusBarInFrame;
31 |
32 | - (id)initWithSourceView:(UIView *)sourceView destinationView:(UIView *)destinationView duration:(NSTimeInterval)duration timingCurve:(UIViewAnimationCurve)timingCurve completionAction:(MPTransitionAction)action {
33 | self = [super init];
34 | if (self)
35 | {
36 | _sourceView = sourceView;
37 | _destinationView = destinationView;
38 | _duration = duration;
39 | _rect = [sourceView bounds];
40 | _timingCurve = timingCurve;
41 | _completionAction = action;
42 | _m34 = INFINITY;
43 | _presentedControllerIncludesStatusBarInFrame = NO;
44 | }
45 |
46 | return self;
47 | }
48 |
49 | #pragma mark - Instance methods
50 |
51 | - (NSString *)timingCurveFunctionName
52 | {
53 | switch ([self timingCurve]) {
54 | case UIViewAnimationCurveEaseOut:
55 | return kCAMediaTimingFunctionEaseOut;
56 |
57 | case UIViewAnimationCurveEaseIn:
58 | return kCAMediaTimingFunctionEaseIn;
59 |
60 | case UIViewAnimationCurveEaseInOut:
61 | return kCAMediaTimingFunctionEaseInEaseOut;
62 |
63 | case UIViewAnimationCurveLinear:
64 | return kCAMediaTimingFunctionLinear;
65 | }
66 |
67 | return kCAMediaTimingFunctionDefault;
68 | }
69 |
70 | - (void)perform
71 | {
72 | [self perform:nil];
73 | }
74 |
75 | - (void)perform:(void (^)(BOOL finished))completion
76 | {
77 | [NSException raise:@"Incomplete Implementation" format:@"MPTransition must be subclassed and the perform: method implemented."];
78 | }
79 |
80 | - (void)transitionDidComplete
81 | {
82 | switch (self.completionAction) {
83 | case MPTransitionActionAddRemove:
84 | [[self.sourceView superview] addSubview:self.destinationView];
85 | [self.sourceView removeFromSuperview];
86 | [self.sourceView setHidden:NO];
87 | break;
88 |
89 | case MPTransitionActionShowHide:
90 | [self.destinationView setHidden:NO];
91 | [self.sourceView setHidden:YES];
92 | break;
93 |
94 | case MPTransitionActionNone:
95 | [self.sourceView setHidden:NO];
96 | break;
97 | }
98 | }
99 |
100 | - (void)setPresentingController:(UIViewController *)presentingController
101 | {
102 | UIViewController *src = (UIViewController *)presentingController;
103 | // find out the presentation context for the presenting view controller
104 | while (YES)// (![src definesPresentationContext])
105 | {
106 | if (![src parentViewController])
107 | break;
108 |
109 | src = [src parentViewController];
110 | }
111 |
112 | [self setSourceView:src.view];
113 | if ([src wantsFullScreenLayout] && NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_6_1)
114 | {
115 | // don't include the status bar height in the rect to fold
116 | CGRect frame = src.view.frame;
117 | CGRect frameViewRect = [src.view convertRect:frame fromView:nil];
118 | CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
119 | CGRect statusBarWindowRect = [src.view.window convertRect:statusBarFrame fromWindow: nil];
120 | CGRect statusBarViewRect = [src.view convertRect:statusBarWindowRect fromView: nil];
121 | frameViewRect.origin.y += statusBarViewRect.size.height;
122 | frameViewRect.size.height -= statusBarViewRect.size.height;
123 | [self setRect:frameViewRect];
124 | }
125 | else
126 | {
127 | [self setRect:src.view.bounds];
128 | }
129 | }
130 |
131 | - (void)setPresentedController:(UIViewController *)presentedController;
132 | {
133 | [self setPresentedControllerIncludesStatusBarInFrame:[presentedController isKindOfClass:[UINavigationController class]]];
134 | }
135 |
136 | - (CGRect)calculateRect
137 | {
138 | CGRect bounds = self.rect;
139 | if ([self isDimissing] && [self presentedControllerIncludesStatusBarInFrame])
140 | {
141 | // we need to remove status bar height from rect (e.g. is UINavigationController)
142 | CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
143 | CGRect statusBarWindowRect = [self.destinationView.window convertRect:statusBarFrame fromWindow: nil];
144 | CGRect statusBarViewRect = [self.destinationView convertRect:statusBarWindowRect fromView: nil];
145 | bounds.origin.y += statusBarViewRect.size.height;
146 | bounds.size.height -= statusBarViewRect.size.height;
147 | [self setRect:bounds];
148 | }
149 |
150 | return bounds;
151 | }
152 |
153 | #pragma mark - Class methods
154 |
155 | + (NSTimeInterval)defaultDuration
156 | {
157 | return DEFAULT_DURATION;
158 | }
159 |
160 | @end
161 |
162 | #pragma mark - UIView extensions
163 |
164 | @implementation UIView(MPAnimation)
165 |
166 | + (BOOL)subView:(UIView *)subView1 isAboveSubView:(UIView *)subView2
167 | {
168 | UIView *superview = [subView1 superview];
169 | int index1 = [[superview subviews] indexOfObject:subView1];
170 | int index2 = [[superview subviews] indexOfObject:subView2];
171 | if (index2 == NSNotFound)
172 | [NSException raise:@"Invalid Operation" format:@"Both views must have the same superview"];
173 | return index1 > index2;
174 | }
175 |
176 | + (BOOL)subView:(UIView *)subView1 isBelowSubView:(UIView *)subView2
177 | {
178 | return [self subView:subView2 isAboveSubView:subView1];
179 | }
180 |
181 | - (BOOL)isAboveSiblingView:(UIView *)siblingView
182 | {
183 | return [UIView subView:self isAboveSubView:siblingView];
184 | }
185 |
186 | - (BOOL)isBelowSiblingView:(UIView *)siblingView
187 | {
188 | return [UIView subView:siblingView isAboveSubView:self];
189 | }
190 |
191 | @end
192 |
--------------------------------------------------------------------------------
/MPFoldTransition/ViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // MPTransition (v 1.1.4)
4 | //
5 | // Created by Mark Pospesel on 4/20/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #import "ViewController.h"
10 | #import "MPFoldTransition.h"
11 | #import "MPFlipTransition.h"
12 | #import "AppDelegate.h"
13 | #import "MPFoldSegue.h"
14 | #import "DetailsViewController.h"
15 | #import
16 |
17 | #define ABOUT_IDENTIFIER @"AboutID"
18 | #define DETAILS_IDENTIFIER @"DetailsID"
19 | #define ABOUT_SEGUE_IDENTIFIER @"segueToAbout"
20 | #define DETAILS_SEGUE_IDENTIFIER @"segueToDetails"
21 | #define STYLE_TABLE_IDENTIFIER @"StyleTableID"
22 | #define FOLD_STYLE_TABLE_IDENTIFIER @"FoldStyleTableID"
23 | #define FLIP_STYLE_TABLE_IDENTIFIER @"FlipStyleTableID"
24 |
25 | @interface ViewController ()
26 |
27 | @property (strong, nonatomic) UIPopoverController *popover;
28 |
29 | @end
30 |
31 | @implementation ViewController
32 |
33 | @synthesize mode = _mode;
34 | @synthesize foldStyle = _foldStyle;
35 | @synthesize flipStyle = _flipStyle;
36 | @synthesize contentView = _contentView;
37 | @synthesize modeSegment = _modeSegment;
38 | @synthesize popover = _popover;
39 |
40 | - (id)init
41 | {
42 | self = [super init];
43 | if (self)
44 | {
45 | [self doInit];
46 | }
47 |
48 | return self;
49 | }
50 |
51 | - (id)initWithCoder:(NSCoder *)aDecoder
52 | {
53 | self = [super initWithCoder:aDecoder];
54 | if (self)
55 | {
56 | [self doInit];
57 | }
58 |
59 | return self;
60 |
61 | }
62 |
63 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
64 | {
65 | self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
66 | if (self)
67 | {
68 | [self doInit];
69 | }
70 |
71 | return self;
72 |
73 | }
74 |
75 | - (void)doInit
76 | {
77 | _mode = MPTransitionModeFold;
78 | _foldStyle = MPFoldStyleCubic;
79 | _flipStyle = MPFlipStyleDefault;
80 | }
81 |
82 | #pragma mark - Properties
83 |
84 | - (NSUInteger)style
85 | {
86 | switch ([self mode]) {
87 | case MPTransitionModeFold:
88 | return [self foldStyle];
89 |
90 | case MPTransitionModeFlip:
91 | return [self flipStyle];
92 | }
93 | }
94 |
95 | - (void)setStyle:(NSUInteger)style
96 | {
97 | switch ([self mode]) {
98 | case MPTransitionModeFold:
99 | [self setFoldStyle:style];
100 | break;
101 |
102 | case MPTransitionModeFlip:
103 | [self setFlipStyle:style];
104 | break;
105 | }
106 | }
107 |
108 | - (BOOL)isFold
109 | {
110 | return [self mode] == MPTransitionModeFold;
111 | }
112 |
113 | #pragma mark - View Lifecycle
114 |
115 | - (void)viewDidLoad
116 | {
117 | [super viewDidLoad];
118 | // Do any additional setup after loading the view, typically from a nib.
119 | [self.modeSegment setSelectedSegmentIndex:(int)[self mode]];
120 | [self.contentView addSubview:[self getLabelForIndex:0]];
121 | }
122 |
123 | - (void)viewDidUnload
124 | {
125 | [self setContentView:nil];
126 | [self setModeSegment:nil];
127 | [super viewDidUnload];
128 | // Release any retained subviews of the main view.
129 | }
130 |
131 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
132 | {
133 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
134 | return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
135 | } else {
136 | return YES;
137 | }
138 | }
139 |
140 | #pragma mark - Instance methods
141 |
142 | - (UIView *)getLabelForIndex:(NSUInteger)index
143 | {
144 | UIView *container = [[UIView alloc] initWithFrame:self.contentView.bounds];
145 | container.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
146 | [container setBackgroundColor:[UIColor whiteColor]];
147 |
148 | UILabel *label = [[UILabel alloc] initWithFrame:CGRectInset(container.bounds, 10, 10)];
149 | label.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
150 | [label setFont:[UIFont boldSystemFontOfSize:84]];
151 | [label setTextAlignment:UITextAlignmentCenter];
152 | [label setTextColor:[UIColor lightTextColor]];
153 | label.text = [NSString stringWithFormat:@"%d", index + 1];
154 |
155 | switch (index % 6) {
156 | case 0:
157 | [label setBackgroundColor:[UIColor redColor]];
158 | break;
159 |
160 | case 1:
161 | [label setBackgroundColor:[UIColor orangeColor]];
162 | break;
163 |
164 | case 2:
165 | [label setBackgroundColor:[UIColor yellowColor]];
166 | [label setTextColor:[UIColor darkTextColor]];
167 | break;
168 |
169 | case 3:
170 | [label setBackgroundColor:[UIColor greenColor]];
171 | [label setTextColor:[UIColor darkTextColor]];
172 | break;
173 |
174 | case 4:
175 | [label setBackgroundColor:[UIColor blueColor]];
176 | break;
177 |
178 | case 5:
179 | [label setBackgroundColor:[UIColor purpleColor]];
180 | break;
181 |
182 | default:
183 | break;
184 | }
185 |
186 | [container addSubview:label];
187 | container.tag = index;
188 | [container.layer setBorderColor:[[UIColor colorWithWhite:0.85 alpha:1] CGColor]];
189 | [container.layer setBorderWidth:2];
190 |
191 | return container;
192 | }
193 |
194 | - (void)updateClipsToBounds
195 | {
196 | // We want clipsToBounds == YES on the central contentView when fold style mode bit is not cubic
197 | // Otherwise you see the top & bottom panels sliding out and looks weird
198 | [self.contentView setClipsToBounds:[self isFold] && (([self foldStyle] & MPFoldStyleCubic) != MPFoldStyleCubic)];
199 | }
200 |
201 | #pragma mark - Touch handlers
202 |
203 | - (IBAction)stepperValueChanged:(id)sender {
204 | UIStepper *stepper = sender;
205 | [stepper setUserInteractionEnabled:NO];
206 | UIView *previousView = [[self.contentView subviews] objectAtIndex:0];
207 | UIView *nextView = [self getLabelForIndex:stepper.value];
208 | BOOL forwards = nextView.tag > previousView.tag;
209 | // handle wrap around
210 | if (nextView.tag == stepper.maximumValue && previousView.tag == stepper.minimumValue)
211 | forwards = NO;
212 | else if (nextView.tag == stepper.minimumValue && previousView.tag == stepper.maximumValue)
213 | forwards = YES;
214 |
215 | // execute the transition
216 | if ([self isFold])
217 | {
218 | [MPFoldTransition transitionFromView:previousView
219 | toView:nextView
220 | duration:[MPFoldTransition defaultDuration]
221 | style:forwards? [self foldStyle] : MPFoldStyleFlipFoldBit([self foldStyle])
222 | transitionAction:MPTransitionActionAddRemove
223 | completion:^(BOOL finished) {
224 | [stepper setUserInteractionEnabled:YES];
225 | }
226 | ];
227 | }
228 | else
229 | {
230 | [MPFlipTransition transitionFromView:previousView
231 | toView:nextView
232 | duration:[MPTransition defaultDuration]
233 | style:forwards? [self flipStyle] : MPFlipStyleFlipDirectionBit([self flipStyle])
234 | transitionAction:MPTransitionActionAddRemove
235 | completion:^(BOOL finished) {
236 | [stepper setUserInteractionEnabled:YES];
237 | }
238 | ];
239 | }
240 | }
241 |
242 | - (IBAction)infoPressed:(UIBarButtonItem *)sender {
243 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:[AppDelegate storyboardName] bundle:nil];
244 | AboutViewController *about = [storyboard instantiateViewControllerWithIdentifier:ABOUT_IDENTIFIER];
245 | [about setModalDelegate:self];
246 | about.title = @"About";
247 | UIViewController *presented = about;
248 |
249 | /*int i = arc4random_uniform(3);
250 | switch (i) {
251 | case 1:
252 | // Embed in nav controller
253 | presented = [[UINavigationController alloc] initWithRootViewController:about];
254 | break;
255 |
256 | case 2:
257 | {
258 | // Embed in tab controller along with Details screen
259 | DetailsViewController *details = [storyboard instantiateViewControllerWithIdentifier:DETAILS_IDENTIFIER];
260 | [details setFold:[self isFold]];
261 | [details setStyle:[self style]];
262 | details.title = @"Details";
263 | UITabBarController *tab = [[UITabBarController alloc] init];
264 | [tab setViewControllers:[NSArray arrayWithObjects:about, details, nil] animated:NO];
265 | [tab setSelectedIndex:arc4random_uniform(2)];
266 | presented = tab;
267 | }
268 | break;
269 | }*/
270 |
271 | if ([self isFold])
272 | [self presentViewController:presented foldStyle:[self foldStyle] completion:nil];
273 | else
274 | [self presentViewController:presented flipStyle:[self flipStyle] completion:nil];
275 | }
276 |
277 | - (IBAction)stylePressed:(id)sender {
278 | BOOL isPad = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
279 | // for iPad present Style table as a popover
280 | if (isPad && [self.popover isPopoverVisible])
281 | {
282 | [self.popover dismissPopoverAnimated:YES];
283 | [self setPopover:nil];
284 | return;
285 | }
286 |
287 | BOOL isFold = [self isFold];
288 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:[AppDelegate storyboardName] bundle:nil];
289 | StyleTable *styleTable = [storyboard instantiateViewControllerWithIdentifier:isFold? FOLD_STYLE_TABLE_IDENTIFIER : FLIP_STYLE_TABLE_IDENTIFIER];
290 | [styleTable setFold:isFold];
291 | [styleTable setStyle:[self style]];
292 | [styleTable setStyleDelegate:self];
293 |
294 | if (!isPad)
295 | {
296 | // for iPhone push Style table onto navigation stack (using a fold transition in our current style!)
297 | if (isFold)
298 | [self.navigationController pushViewController:styleTable foldStyle:[self foldStyle]];
299 | else
300 | [self.navigationController pushViewController:styleTable flipStyle:[self flipStyle]];
301 | }
302 | else
303 | {
304 | // for iPad, just use a popover
305 | UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:styleTable];
306 | [self setPopover:[[UIPopoverController alloc] initWithContentViewController:navController]];
307 |
308 | [self.popover setDelegate:self];
309 | [self.popover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
310 | }
311 | }
312 |
313 | - (IBAction)modeValueChanged:(id)sender {
314 | // switch between fold & flip transitions
315 | [self setMode:[sender selectedSegmentIndex]];
316 | [self updateClipsToBounds];
317 | }
318 |
319 | - (IBAction)detailPressed:(id)sender {
320 | BOOL isFold = [self isFold];
321 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:[AppDelegate storyboardName] bundle:nil];
322 | DetailsViewController *details = [storyboard instantiateViewControllerWithIdentifier:DETAILS_IDENTIFIER];
323 | [details setFold:isFold];
324 | [details setStyle:[self style]];
325 |
326 | // push Details view controller onto navigation stack (using a fold or flip transition in our current style!)
327 | if (isFold)
328 | [self.navigationController pushViewController:details foldStyle:[self foldStyle]];
329 | else
330 | [self.navigationController pushViewController:details flipStyle:[self flipStyle]];
331 | }
332 |
333 | #pragma mark - Storyboards
334 |
335 | // I removed the segues from the storyboards so that I could switch between flip and fold segue classes
336 | /*- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
337 | {
338 | // set the selected fold style to our segues
339 | if ([[segue identifier] isEqualToString:DETAILS_SEGUE_IDENTIFIER])
340 | {
341 | DetailsViewController *details = [segue destinationViewController];
342 | [details setStyle:[self foldStyle]];
343 |
344 | MPFoldSegue *foldSegue = (MPFoldSegue *)segue;
345 | [foldSegue setStyle:[self foldStyle]];
346 | }
347 | else if ([[segue identifier] isEqualToString:ABOUT_SEGUE_IDENTIFIER])
348 | {
349 | AboutViewController *about = [segue destinationViewController];
350 | [about setModalDelegate:self];
351 |
352 | MPFoldSegue *foldSegue = (MPFoldSegue *)segue;
353 | [foldSegue setStyle:[self foldStyle]];
354 | }
355 | }*/
356 |
357 | #pragma mark - MPModalViewControllerDelegate
358 |
359 | - (void)dismiss
360 | {
361 | // use the opposite fold style from the transition that presented the modal view
362 | if ([self isFold])
363 | {
364 | MPFoldStyle dismissStyle = MPFoldStyleFlipFoldBit([self foldStyle]);
365 |
366 | [self dismissViewControllerWithFoldStyle:dismissStyle completion:nil];
367 | }
368 | else
369 | {
370 | MPFlipStyle dismissStyle = MPFlipStyleFlipDirectionBit([self flipStyle]);
371 | [self dismissViewControllerWithFlipStyle:dismissStyle completion:nil];
372 | }
373 | }
374 |
375 | #pragma mark - UIPopoverControllerDelegate
376 |
377 | - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
378 | {
379 | [self setPopover:nil];
380 | }
381 |
382 | #pragma mark - StyleDelegate
383 |
384 | - (void)styleDidChange:(NSUInteger)newStyle
385 | {
386 | [self setStyle:newStyle];
387 | [self updateClipsToBounds];
388 | }
389 |
390 | @end
391 |
--------------------------------------------------------------------------------
/MPFoldTransition.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 4D63A174154FEE0D00B310CF /* StyleTable.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D63A173154FEE0D00B310CF /* StyleTable.m */; };
11 | 4DB921531561117B0080D608 /* MPTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DB921521561117B0080D608 /* MPTransition.m */; };
12 | 4DB9BF1E1553E36200718F74 /* Icon-72.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DB9BF1A1553E36200718F74 /* Icon-72.png */; };
13 | 4DB9BF1F1553E36200718F74 /* Icon-72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DB9BF1B1553E36200718F74 /* Icon-72@2x.png */; };
14 | 4DB9BF201553E36200718F74 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DB9BF1C1553E36200718F74 /* Icon.png */; };
15 | 4DB9BF211553E36200718F74 /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DB9BF1D1553E36200718F74 /* Icon@2x.png */; };
16 | 4DBA43C515639EA800D02824 /* MPFlipSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBA43C015639C5100D02824 /* MPFlipSegue.m */; };
17 | 4DBDFA0915415F0A003AD415 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBDFA0815415F0A003AD415 /* UIKit.framework */; };
18 | 4DBDFA0B15415F0A003AD415 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBDFA0A15415F0A003AD415 /* Foundation.framework */; };
19 | 4DBDFA0D15415F0A003AD415 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBDFA0C15415F0A003AD415 /* CoreGraphics.framework */; };
20 | 4DBDFA1315415F0A003AD415 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 4DBDFA1115415F0A003AD415 /* InfoPlist.strings */; };
21 | 4DBDFA1515415F0A003AD415 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA1415415F0A003AD415 /* main.m */; };
22 | 4DBDFA1915415F0A003AD415 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA1815415F0A003AD415 /* AppDelegate.m */; };
23 | 4DBDFA1C15415F0A003AD415 /* MainStoryboard_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DBDFA1A15415F0A003AD415 /* MainStoryboard_iPhone.storyboard */; };
24 | 4DBDFA1F15415F0A003AD415 /* MainStoryboard_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4DBDFA1D15415F0A003AD415 /* MainStoryboard_iPad.storyboard */; };
25 | 4DBDFA2215415F0A003AD415 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA2115415F0A003AD415 /* ViewController.m */; };
26 | 4DBDFA2E15416013003AD415 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBDFA2D15416013003AD415 /* QuartzCore.framework */; };
27 | 4DBDFA3E15416BDA003AD415 /* MPAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA3315416BAD003AD415 /* MPAnimation.m */; };
28 | 4DBDFA3F15416BDA003AD415 /* MPFoldModalSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA3515416BAD003AD415 /* MPFoldModalSegue.m */; };
29 | 4DBDFA4015416BDA003AD415 /* MPFoldNavPopSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA3715416BAD003AD415 /* MPFoldNavPopSegue.m */; };
30 | 4DBDFA4115416BDA003AD415 /* MPFoldNavPushSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA3915416BAD003AD415 /* MPFoldNavPushSegue.m */; };
31 | 4DBDFA4215416BDA003AD415 /* MPFoldSegue.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA3B15416BAD003AD415 /* MPFoldSegue.m */; };
32 | 4DBDFA4315416BDA003AD415 /* MPFoldTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DBDFA3D15416BAD003AD415 /* MPFoldTransition.m */; };
33 | 4DC8CD04160456BB00C42353 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 4DC8CD03160456BB00C42353 /* Default-568h@2x.png */; };
34 | 4DEBF71A1562587100553D98 /* MPFlipTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 4DEBF7191562587100553D98 /* MPFlipTransition.m */; };
35 | 4DFA2F0F154FF49D004AF678 /* AboutViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D63A177154FEE8500B310CF /* AboutViewController.m */; };
36 | 4DFA2F11154FF4A1004AF678 /* DetailsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4D63A179154FEE8600B310CF /* DetailsViewController.m */; };
37 | /* End PBXBuildFile section */
38 |
39 | /* Begin PBXFileReference section */
40 | 4D63A172154FEE0D00B310CF /* StyleTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = StyleTable.h; path = MPFoldTransition/StyleTable.h; sourceTree = ""; };
41 | 4D63A173154FEE0D00B310CF /* StyleTable.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = StyleTable.m; path = MPFoldTransition/StyleTable.m; sourceTree = ""; };
42 | 4D63A176154FEE8500B310CF /* AboutViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AboutViewController.h; path = MPFoldTransition/AboutViewController.h; sourceTree = ""; };
43 | 4D63A177154FEE8500B310CF /* AboutViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AboutViewController.m; path = MPFoldTransition/AboutViewController.m; sourceTree = ""; };
44 | 4D63A178154FEE8600B310CF /* DetailsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DetailsViewController.h; path = MPFoldTransition/DetailsViewController.h; sourceTree = ""; };
45 | 4D63A179154FEE8600B310CF /* DetailsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = DetailsViewController.m; path = MPFoldTransition/DetailsViewController.m; sourceTree = ""; };
46 | 4DB921511561117B0080D608 /* MPTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTransition.h; sourceTree = ""; };
47 | 4DB921521561117B0080D608 /* MPTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPTransition.m; sourceTree = ""; };
48 | 4DB92155156111C00080D608 /* MPTransitionEnumerations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPTransitionEnumerations.h; sourceTree = ""; };
49 | 4DB9BF1A1553E36200718F74 /* Icon-72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-72.png"; path = "MPFoldTransition/Icon-72.png"; sourceTree = ""; };
50 | 4DB9BF1B1553E36200718F74 /* Icon-72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon-72@2x.png"; path = "MPFoldTransition/Icon-72@2x.png"; sourceTree = ""; };
51 | 4DB9BF1C1553E36200718F74 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Icon.png; path = MPFoldTransition/Icon.png; sourceTree = ""; };
52 | 4DB9BF1D1553E36200718F74 /* Icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Icon@2x.png"; path = "MPFoldTransition/Icon@2x.png"; sourceTree = ""; };
53 | 4DB9BF221553E3A300718F74 /* Source Code License.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = "Source Code License.rtf"; sourceTree = ""; };
54 | 4DB9BF241553EA8A00718F74 /* README.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = README.markdown; sourceTree = ""; };
55 | 4DB9BF251553EA8A00718F74 /* TODO.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = TODO.markdown; sourceTree = ""; };
56 | 4DBA43BF15639C5100D02824 /* MPFlipSegue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFlipSegue.h; sourceTree = ""; };
57 | 4DBA43C015639C5100D02824 /* MPFlipSegue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPFlipSegue.m; sourceTree = ""; };
58 | 4DBDFA0415415F0A003AD415 /* MPFoldTransition.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MPFoldTransition.app; sourceTree = BUILT_PRODUCTS_DIR; };
59 | 4DBDFA0815415F0A003AD415 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
60 | 4DBDFA0A15415F0A003AD415 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
61 | 4DBDFA0C15415F0A003AD415 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
62 | 4DBDFA1015415F0A003AD415 /* MPFoldTransition-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MPFoldTransition-Info.plist"; sourceTree = ""; };
63 | 4DBDFA1215415F0A003AD415 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
64 | 4DBDFA1415415F0A003AD415 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
65 | 4DBDFA1615415F0A003AD415 /* MPFoldTransition-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MPFoldTransition-Prefix.pch"; sourceTree = ""; };
66 | 4DBDFA1715415F0A003AD415 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = MPFoldTransition/AppDelegate.h; sourceTree = ""; };
67 | 4DBDFA1815415F0A003AD415 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = MPFoldTransition/AppDelegate.m; sourceTree = ""; };
68 | 4DBDFA1B15415F0A003AD415 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPhone.storyboard; sourceTree = ""; };
69 | 4DBDFA1E15415F0A003AD415 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard_iPad.storyboard; sourceTree = ""; };
70 | 4DBDFA2015415F0A003AD415 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ViewController.h; path = MPFoldTransition/ViewController.h; sourceTree = ""; };
71 | 4DBDFA2115415F0A003AD415 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ViewController.m; path = MPFoldTransition/ViewController.m; sourceTree = ""; };
72 | 4DBDFA2D15416013003AD415 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
73 | 4DBDFA3215416BAD003AD415 /* MPAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPAnimation.h; sourceTree = ""; };
74 | 4DBDFA3315416BAD003AD415 /* MPAnimation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPAnimation.m; sourceTree = ""; };
75 | 4DBDFA3415416BAD003AD415 /* MPFoldModalSegue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFoldModalSegue.h; sourceTree = ""; };
76 | 4DBDFA3515416BAD003AD415 /* MPFoldModalSegue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPFoldModalSegue.m; sourceTree = ""; };
77 | 4DBDFA3615416BAD003AD415 /* MPFoldNavPopSegue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFoldNavPopSegue.h; sourceTree = ""; };
78 | 4DBDFA3715416BAD003AD415 /* MPFoldNavPopSegue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPFoldNavPopSegue.m; sourceTree = ""; };
79 | 4DBDFA3815416BAD003AD415 /* MPFoldNavPushSegue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFoldNavPushSegue.h; sourceTree = ""; };
80 | 4DBDFA3915416BAD003AD415 /* MPFoldNavPushSegue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPFoldNavPushSegue.m; sourceTree = ""; };
81 | 4DBDFA3A15416BAD003AD415 /* MPFoldSegue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFoldSegue.h; sourceTree = ""; };
82 | 4DBDFA3B15416BAD003AD415 /* MPFoldSegue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPFoldSegue.m; sourceTree = ""; };
83 | 4DBDFA3C15416BAD003AD415 /* MPFoldTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFoldTransition.h; sourceTree = ""; };
84 | 4DBDFA3D15416BAD003AD415 /* MPFoldTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPFoldTransition.m; sourceTree = ""; };
85 | 4DC8CD03160456BB00C42353 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; };
86 | 4DDCC8E2154960C500489938 /* MPFoldEnumerations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFoldEnumerations.h; sourceTree = ""; };
87 | 4DEBF7181562587100553D98 /* MPFlipTransition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPFlipTransition.h; sourceTree = ""; };
88 | 4DEBF7191562587100553D98 /* MPFlipTransition.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPFlipTransition.m; sourceTree = ""; };
89 | 4DEBF71C156258E400553D98 /* MPFlipEnumerations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPFlipEnumerations.h; sourceTree = ""; };
90 | /* End PBXFileReference section */
91 |
92 | /* Begin PBXFrameworksBuildPhase section */
93 | 4DBDFA0115415F0A003AD415 /* Frameworks */ = {
94 | isa = PBXFrameworksBuildPhase;
95 | buildActionMask = 2147483647;
96 | files = (
97 | 4DBDFA2E15416013003AD415 /* QuartzCore.framework in Frameworks */,
98 | 4DBDFA0915415F0A003AD415 /* UIKit.framework in Frameworks */,
99 | 4DBDFA0B15415F0A003AD415 /* Foundation.framework in Frameworks */,
100 | 4DBDFA0D15415F0A003AD415 /* CoreGraphics.framework in Frameworks */,
101 | );
102 | runOnlyForDeploymentPostprocessing = 0;
103 | };
104 | /* End PBXFrameworksBuildPhase section */
105 |
106 | /* Begin PBXGroup section */
107 | 4DB9BF121553DBC900718F74 /* Segues */ = {
108 | isa = PBXGroup;
109 | children = (
110 | 4DBDFA3A15416BAD003AD415 /* MPFoldSegue.h */,
111 | 4DBDFA3B15416BAD003AD415 /* MPFoldSegue.m */,
112 | 4DBDFA3415416BAD003AD415 /* MPFoldModalSegue.h */,
113 | 4DBDFA3515416BAD003AD415 /* MPFoldModalSegue.m */,
114 | 4DBDFA3615416BAD003AD415 /* MPFoldNavPopSegue.h */,
115 | 4DBDFA3715416BAD003AD415 /* MPFoldNavPopSegue.m */,
116 | 4DBDFA3815416BAD003AD415 /* MPFoldNavPushSegue.h */,
117 | 4DBDFA3915416BAD003AD415 /* MPFoldNavPushSegue.m */,
118 | 4DBA43BF15639C5100D02824 /* MPFlipSegue.h */,
119 | 4DBA43C015639C5100D02824 /* MPFlipSegue.m */,
120 | );
121 | path = Segues;
122 | sourceTree = "";
123 | };
124 | 4DB9BF191553E25D00718F74 /* Resources */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 4DBDFA1A15415F0A003AD415 /* MainStoryboard_iPhone.storyboard */,
128 | 4DBDFA1D15415F0A003AD415 /* MainStoryboard_iPad.storyboard */,
129 | 4DB9BF1A1553E36200718F74 /* Icon-72.png */,
130 | 4DB9BF1B1553E36200718F74 /* Icon-72@2x.png */,
131 | 4DB9BF1C1553E36200718F74 /* Icon.png */,
132 | 4DB9BF1D1553E36200718F74 /* Icon@2x.png */,
133 | );
134 | name = Resources;
135 | sourceTree = "";
136 | };
137 | 4DBDF9F915415F09003AD415 = {
138 | isa = PBXGroup;
139 | children = (
140 | 4DC8CD03160456BB00C42353 /* Default-568h@2x.png */,
141 | 4DBDFA2C15415FE1003AD415 /* Demo */,
142 | 4DBDFA0E15415F0A003AD415 /* MPFoldTransition */,
143 | 4DBDFA0715415F0A003AD415 /* Frameworks */,
144 | 4DBDFA0515415F0A003AD415 /* Products */,
145 | );
146 | sourceTree = "";
147 | };
148 | 4DBDFA0515415F0A003AD415 /* Products */ = {
149 | isa = PBXGroup;
150 | children = (
151 | 4DBDFA0415415F0A003AD415 /* MPFoldTransition.app */,
152 | );
153 | name = Products;
154 | sourceTree = "";
155 | };
156 | 4DBDFA0715415F0A003AD415 /* Frameworks */ = {
157 | isa = PBXGroup;
158 | children = (
159 | 4DBDFA2D15416013003AD415 /* QuartzCore.framework */,
160 | 4DBDFA0815415F0A003AD415 /* UIKit.framework */,
161 | 4DBDFA0A15415F0A003AD415 /* Foundation.framework */,
162 | 4DBDFA0C15415F0A003AD415 /* CoreGraphics.framework */,
163 | );
164 | name = Frameworks;
165 | sourceTree = "";
166 | };
167 | 4DBDFA0E15415F0A003AD415 /* MPFoldTransition */ = {
168 | isa = PBXGroup;
169 | children = (
170 | 4DB9BF121553DBC900718F74 /* Segues */,
171 | 4DB92155156111C00080D608 /* MPTransitionEnumerations.h */,
172 | 4DDCC8E2154960C500489938 /* MPFoldEnumerations.h */,
173 | 4DBDFA3215416BAD003AD415 /* MPAnimation.h */,
174 | 4DBDFA3315416BAD003AD415 /* MPAnimation.m */,
175 | 4DBDFA3C15416BAD003AD415 /* MPFoldTransition.h */,
176 | 4DBDFA3D15416BAD003AD415 /* MPFoldTransition.m */,
177 | 4DB921511561117B0080D608 /* MPTransition.h */,
178 | 4DB921521561117B0080D608 /* MPTransition.m */,
179 | 4DEBF71C156258E400553D98 /* MPFlipEnumerations.h */,
180 | 4DEBF7181562587100553D98 /* MPFlipTransition.h */,
181 | 4DEBF7191562587100553D98 /* MPFlipTransition.m */,
182 | );
183 | name = MPFoldTransition;
184 | path = MPFoldTransition/MPFoldTransition;
185 | sourceTree = "";
186 | };
187 | 4DBDFA0F15415F0A003AD415 /* Supporting Files */ = {
188 | isa = PBXGroup;
189 | children = (
190 | 4DBDFA1015415F0A003AD415 /* MPFoldTransition-Info.plist */,
191 | 4DBDFA1115415F0A003AD415 /* InfoPlist.strings */,
192 | 4DBDFA1415415F0A003AD415 /* main.m */,
193 | 4DBDFA1615415F0A003AD415 /* MPFoldTransition-Prefix.pch */,
194 | );
195 | name = "Supporting Files";
196 | path = MPFoldTransition;
197 | sourceTree = "";
198 | };
199 | 4DBDFA2C15415FE1003AD415 /* Demo */ = {
200 | isa = PBXGroup;
201 | children = (
202 | 4DB9BF241553EA8A00718F74 /* README.markdown */,
203 | 4DB9BF221553E3A300718F74 /* Source Code License.rtf */,
204 | 4DB9BF251553EA8A00718F74 /* TODO.markdown */,
205 | 4DB9BF191553E25D00718F74 /* Resources */,
206 | 4DBDFA0F15415F0A003AD415 /* Supporting Files */,
207 | 4DBDFA1715415F0A003AD415 /* AppDelegate.h */,
208 | 4DBDFA1815415F0A003AD415 /* AppDelegate.m */,
209 | 4DBDFA2015415F0A003AD415 /* ViewController.h */,
210 | 4DBDFA2115415F0A003AD415 /* ViewController.m */,
211 | 4D63A176154FEE8500B310CF /* AboutViewController.h */,
212 | 4D63A177154FEE8500B310CF /* AboutViewController.m */,
213 | 4D63A178154FEE8600B310CF /* DetailsViewController.h */,
214 | 4D63A179154FEE8600B310CF /* DetailsViewController.m */,
215 | 4D63A172154FEE0D00B310CF /* StyleTable.h */,
216 | 4D63A173154FEE0D00B310CF /* StyleTable.m */,
217 | );
218 | name = Demo;
219 | sourceTree = "";
220 | };
221 | /* End PBXGroup section */
222 |
223 | /* Begin PBXNativeTarget section */
224 | 4DBDFA0315415F0A003AD415 /* MPFoldTransition */ = {
225 | isa = PBXNativeTarget;
226 | buildConfigurationList = 4DBDFA2515415F0A003AD415 /* Build configuration list for PBXNativeTarget "MPFoldTransition" */;
227 | buildPhases = (
228 | 4DBDFA0015415F0A003AD415 /* Sources */,
229 | 4DBDFA0115415F0A003AD415 /* Frameworks */,
230 | 4DBDFA0215415F0A003AD415 /* Resources */,
231 | );
232 | buildRules = (
233 | );
234 | dependencies = (
235 | );
236 | name = MPFoldTransition;
237 | productName = MPFoldTransition;
238 | productReference = 4DBDFA0415415F0A003AD415 /* MPFoldTransition.app */;
239 | productType = "com.apple.product-type.application";
240 | };
241 | /* End PBXNativeTarget section */
242 |
243 | /* Begin PBXProject section */
244 | 4DBDF9FB15415F09003AD415 /* Project object */ = {
245 | isa = PBXProject;
246 | attributes = {
247 | LastUpgradeCheck = 0430;
248 | ORGANIZATIONNAME = "Mark Pospesel";
249 | };
250 | buildConfigurationList = 4DBDF9FE15415F09003AD415 /* Build configuration list for PBXProject "MPFoldTransition" */;
251 | compatibilityVersion = "Xcode 3.2";
252 | developmentRegion = English;
253 | hasScannedForEncodings = 0;
254 | knownRegions = (
255 | en,
256 | );
257 | mainGroup = 4DBDF9F915415F09003AD415;
258 | productRefGroup = 4DBDFA0515415F0A003AD415 /* Products */;
259 | projectDirPath = "";
260 | projectRoot = "";
261 | targets = (
262 | 4DBDFA0315415F0A003AD415 /* MPFoldTransition */,
263 | );
264 | };
265 | /* End PBXProject section */
266 |
267 | /* Begin PBXResourcesBuildPhase section */
268 | 4DBDFA0215415F0A003AD415 /* Resources */ = {
269 | isa = PBXResourcesBuildPhase;
270 | buildActionMask = 2147483647;
271 | files = (
272 | 4DBDFA1315415F0A003AD415 /* InfoPlist.strings in Resources */,
273 | 4DBDFA1C15415F0A003AD415 /* MainStoryboard_iPhone.storyboard in Resources */,
274 | 4DBDFA1F15415F0A003AD415 /* MainStoryboard_iPad.storyboard in Resources */,
275 | 4DB9BF1E1553E36200718F74 /* Icon-72.png in Resources */,
276 | 4DB9BF1F1553E36200718F74 /* Icon-72@2x.png in Resources */,
277 | 4DB9BF201553E36200718F74 /* Icon.png in Resources */,
278 | 4DB9BF211553E36200718F74 /* Icon@2x.png in Resources */,
279 | 4DC8CD04160456BB00C42353 /* Default-568h@2x.png in Resources */,
280 | );
281 | runOnlyForDeploymentPostprocessing = 0;
282 | };
283 | /* End PBXResourcesBuildPhase section */
284 |
285 | /* Begin PBXSourcesBuildPhase section */
286 | 4DBDFA0015415F0A003AD415 /* Sources */ = {
287 | isa = PBXSourcesBuildPhase;
288 | buildActionMask = 2147483647;
289 | files = (
290 | 4DBDFA1515415F0A003AD415 /* main.m in Sources */,
291 | 4DBDFA1915415F0A003AD415 /* AppDelegate.m in Sources */,
292 | 4DBDFA2215415F0A003AD415 /* ViewController.m in Sources */,
293 | 4DBDFA3E15416BDA003AD415 /* MPAnimation.m in Sources */,
294 | 4DBDFA3F15416BDA003AD415 /* MPFoldModalSegue.m in Sources */,
295 | 4DBDFA4015416BDA003AD415 /* MPFoldNavPopSegue.m in Sources */,
296 | 4DBDFA4115416BDA003AD415 /* MPFoldNavPushSegue.m in Sources */,
297 | 4DBDFA4215416BDA003AD415 /* MPFoldSegue.m in Sources */,
298 | 4DBDFA4315416BDA003AD415 /* MPFoldTransition.m in Sources */,
299 | 4D63A174154FEE0D00B310CF /* StyleTable.m in Sources */,
300 | 4DFA2F0F154FF49D004AF678 /* AboutViewController.m in Sources */,
301 | 4DFA2F11154FF4A1004AF678 /* DetailsViewController.m in Sources */,
302 | 4DB921531561117B0080D608 /* MPTransition.m in Sources */,
303 | 4DEBF71A1562587100553D98 /* MPFlipTransition.m in Sources */,
304 | 4DBA43C515639EA800D02824 /* MPFlipSegue.m in Sources */,
305 | );
306 | runOnlyForDeploymentPostprocessing = 0;
307 | };
308 | /* End PBXSourcesBuildPhase section */
309 |
310 | /* Begin PBXVariantGroup section */
311 | 4DBDFA1115415F0A003AD415 /* InfoPlist.strings */ = {
312 | isa = PBXVariantGroup;
313 | children = (
314 | 4DBDFA1215415F0A003AD415 /* en */,
315 | );
316 | name = InfoPlist.strings;
317 | sourceTree = "";
318 | };
319 | 4DBDFA1A15415F0A003AD415 /* MainStoryboard_iPhone.storyboard */ = {
320 | isa = PBXVariantGroup;
321 | children = (
322 | 4DBDFA1B15415F0A003AD415 /* en */,
323 | );
324 | name = MainStoryboard_iPhone.storyboard;
325 | path = MPFoldTransition;
326 | sourceTree = "";
327 | };
328 | 4DBDFA1D15415F0A003AD415 /* MainStoryboard_iPad.storyboard */ = {
329 | isa = PBXVariantGroup;
330 | children = (
331 | 4DBDFA1E15415F0A003AD415 /* en */,
332 | );
333 | name = MainStoryboard_iPad.storyboard;
334 | path = MPFoldTransition;
335 | sourceTree = "";
336 | };
337 | /* End PBXVariantGroup section */
338 |
339 | /* Begin XCBuildConfiguration section */
340 | 4DBDFA2315415F0A003AD415 /* Debug */ = {
341 | isa = XCBuildConfiguration;
342 | buildSettings = {
343 | ALWAYS_SEARCH_USER_PATHS = NO;
344 | ARCHS = "$(ARCHS_STANDARD_32_BIT)";
345 | CLANG_ENABLE_OBJC_ARC = YES;
346 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
347 | COPY_PHASE_STRIP = NO;
348 | GCC_C_LANGUAGE_STANDARD = gnu99;
349 | GCC_DYNAMIC_NO_PIC = NO;
350 | GCC_OPTIMIZATION_LEVEL = 0;
351 | GCC_PREPROCESSOR_DEFINITIONS = (
352 | "DEBUG=1",
353 | "$(inherited)",
354 | );
355 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
356 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
357 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
358 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
359 | GCC_WARN_UNUSED_VARIABLE = YES;
360 | IPHONEOS_DEPLOYMENT_TARGET = 5.1;
361 | SDKROOT = iphoneos;
362 | TARGETED_DEVICE_FAMILY = "1,2";
363 | };
364 | name = Debug;
365 | };
366 | 4DBDFA2415415F0A003AD415 /* Release */ = {
367 | isa = XCBuildConfiguration;
368 | buildSettings = {
369 | ALWAYS_SEARCH_USER_PATHS = NO;
370 | ARCHS = "$(ARCHS_STANDARD_32_BIT)";
371 | CLANG_ENABLE_OBJC_ARC = YES;
372 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
373 | COPY_PHASE_STRIP = YES;
374 | GCC_C_LANGUAGE_STANDARD = gnu99;
375 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
376 | GCC_WARN_ABOUT_RETURN_TYPE = YES;
377 | GCC_WARN_UNINITIALIZED_AUTOS = YES;
378 | GCC_WARN_UNUSED_VARIABLE = YES;
379 | IPHONEOS_DEPLOYMENT_TARGET = 5.1;
380 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
381 | SDKROOT = iphoneos;
382 | TARGETED_DEVICE_FAMILY = "1,2";
383 | VALIDATE_PRODUCT = YES;
384 | };
385 | name = Release;
386 | };
387 | 4DBDFA2615415F0A003AD415 /* Debug */ = {
388 | isa = XCBuildConfiguration;
389 | buildSettings = {
390 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
391 | GCC_PREFIX_HEADER = "MPFoldTransition/MPFoldTransition-Prefix.pch";
392 | INFOPLIST_FILE = "MPFoldTransition/MPFoldTransition-Info.plist";
393 | IPHONEOS_DEPLOYMENT_TARGET = 5.0;
394 | PRODUCT_NAME = "$(TARGET_NAME)";
395 | WRAPPER_EXTENSION = app;
396 | };
397 | name = Debug;
398 | };
399 | 4DBDFA2715415F0A003AD415 /* Release */ = {
400 | isa = XCBuildConfiguration;
401 | buildSettings = {
402 | GCC_PRECOMPILE_PREFIX_HEADER = YES;
403 | GCC_PREFIX_HEADER = "MPFoldTransition/MPFoldTransition-Prefix.pch";
404 | INFOPLIST_FILE = "MPFoldTransition/MPFoldTransition-Info.plist";
405 | IPHONEOS_DEPLOYMENT_TARGET = 5.0;
406 | PRODUCT_NAME = "$(TARGET_NAME)";
407 | WRAPPER_EXTENSION = app;
408 | };
409 | name = Release;
410 | };
411 | /* End XCBuildConfiguration section */
412 |
413 | /* Begin XCConfigurationList section */
414 | 4DBDF9FE15415F09003AD415 /* Build configuration list for PBXProject "MPFoldTransition" */ = {
415 | isa = XCConfigurationList;
416 | buildConfigurations = (
417 | 4DBDFA2315415F0A003AD415 /* Debug */,
418 | 4DBDFA2415415F0A003AD415 /* Release */,
419 | );
420 | defaultConfigurationIsVisible = 0;
421 | defaultConfigurationName = Release;
422 | };
423 | 4DBDFA2515415F0A003AD415 /* Build configuration list for PBXNativeTarget "MPFoldTransition" */ = {
424 | isa = XCConfigurationList;
425 | buildConfigurations = (
426 | 4DBDFA2615415F0A003AD415 /* Debug */,
427 | 4DBDFA2715415F0A003AD415 /* Release */,
428 | );
429 | defaultConfigurationIsVisible = 0;
430 | defaultConfigurationName = Release;
431 | };
432 | /* End XCConfigurationList section */
433 | };
434 | rootObject = 4DBDF9FB15415F09003AD415 /* Project object */;
435 | }
436 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPFoldTransition.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFoldTransition.m
3 | // MPFoldTransition (v1.0.2)
4 | //
5 | // Created by Mark Pospesel on 4/4/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #define DEFAULT_SHADOW_OPACITY 0.25
10 |
11 | #import "MPFoldTransition.h"
12 | #import "MPAnimation.h"
13 | #import
14 | #include
15 |
16 | static inline double mp_radians (double degrees) {return degrees * M_PI/180;}
17 |
18 | @interface MPFoldTransition()
19 |
20 | @end
21 |
22 | @implementation MPFoldTransition
23 |
24 | #pragma mark - Properties
25 |
26 | @synthesize style = _style;
27 | @synthesize foldShadowOpacity = _foldShadowOpacity;
28 | @synthesize foldShadowColor = _foldShadowColor;
29 |
30 | #pragma mark - init
31 |
32 | - (id)initWithSourceView:(UIView *)sourceView destinationView:(UIView *)destinationView duration:(NSTimeInterval)duration style:(MPFoldStyle)style completionAction:(MPTransitionAction)action {
33 | self = [super initWithSourceView:sourceView destinationView:destinationView duration:duration timingCurve:(((style & MPFoldStyleUnfold) == MPFoldStyleUnfold)? UIViewAnimationCurveEaseIn : UIViewAnimationCurveEaseOut) completionAction:action];
34 | if (self)
35 | {
36 | _style = style;
37 | _foldShadowOpacity = DEFAULT_SHADOW_OPACITY;
38 | _foldShadowColor = [UIColor blackColor];
39 | }
40 |
41 | return self;
42 | }
43 |
44 | #pragma mark - Instance methods
45 |
46 | - (void)perform:(void (^)(BOOL finished))completion
47 | {
48 | BOOL forwards = ([self style] & MPFoldStyleUnfold) != MPFoldStyleUnfold;
49 | BOOL vertical = ([self style] & MPFoldStyleHorizontal) != MPFoldStyleHorizontal;
50 | BOOL cubic = ([self style] & MPFoldStyleCubic) == MPFoldStyleCubic;
51 |
52 | CGRect bounds = [self calculateRect];
53 | CGFloat scale = [[UIScreen mainScreen] scale];
54 |
55 | // we inset the folding panels 1 point on each side with a transparent margin to antialiase the edges
56 | UIEdgeInsets foldInsets = vertical? UIEdgeInsetsMake(0, 1, 0, 1) : UIEdgeInsetsMake(1, 0, 1, 0);
57 | // for cubic fold, we do the same for the sliding panels
58 | UIEdgeInsets slideInsets = cubic? foldInsets : UIEdgeInsetsZero;
59 | CGFloat slideInsetWidth = cubic? 2 : 0;
60 |
61 | CGRect upperRect = bounds;
62 | if (vertical)
63 | upperRect.size.height = bounds.size.height / 2;
64 | else
65 | upperRect.size.width = bounds.size.width / 2;
66 | CGRect lowerRect = upperRect;
67 | BOOL isOddSize = vertical? (upperRect.size.height != (roundf(upperRect.size.height * scale)/scale)) : (upperRect.size.width != (roundf(upperRect.size.width * scale) / scale));
68 | if (isOddSize)
69 | {
70 | // If view has an odd height, make the 2 panels of integer height with top panel 1 pixel taller (see below)
71 | if (vertical)
72 | {
73 | upperRect.size.height = (roundf(upperRect.size.height * scale)/scale);
74 | lowerRect.size.height = bounds.size.height - upperRect.size.height;
75 | }
76 | else
77 | {
78 | upperRect.size.width = (roundf(upperRect.size.width * scale)/scale);
79 | lowerRect.size.width = bounds.size.width - upperRect.size.width;
80 | }
81 | }
82 | if (vertical)
83 | lowerRect.origin.y += upperRect.size.height;
84 | else
85 | lowerRect.origin.x += upperRect.size.width;
86 |
87 | if (![self isDimissing])
88 | {
89 | if ([self presentedControllerIncludesStatusBarInFrame])
90 | self.destinationView.bounds = CGRectMake(0, 0, bounds.size.width + bounds.origin.x, bounds.size.height + bounds.origin.y);
91 | else
92 | self.destinationView.bounds = (CGRect){CGPointZero, bounds.size};
93 | }
94 |
95 | CGRect destUpperRect = CGRectOffset(upperRect, -upperRect.origin.x, -upperRect.origin.y);
96 | CGRect destLowerRect = CGRectOffset(lowerRect, -upperRect.origin.x, -upperRect.origin.y);
97 |
98 | if ([self isDimissing])
99 | {
100 | CGFloat x = self.destinationView.bounds.size.width - bounds.size.width;
101 | CGFloat y = self.destinationView.bounds.size.height - bounds.size.height;
102 | destUpperRect.origin.x += x;
103 | destLowerRect.origin.x += x;
104 | destUpperRect.origin.y += y;
105 | destLowerRect.origin.y += y;
106 | if (![self presentedControllerIncludesStatusBarInFrame])
107 | [self setRect:CGRectOffset([self rect], x, y)];
108 | }
109 | else if ([self presentedControllerIncludesStatusBarInFrame])
110 | {
111 | destUpperRect.origin.x += bounds.origin.x;
112 | destLowerRect.origin.x += bounds.origin.x;
113 | destUpperRect.origin.y += bounds.origin.y;
114 | destLowerRect.origin.y += bounds.origin.y;
115 | }
116 |
117 | // Create 4 images to represent 2 halves of the 2 views
118 | UIImage * foldUpper = [MPAnimation renderImageFromView:forwards? self.sourceView : self.destinationView withRect:forwards? upperRect : destUpperRect transparentInsets:foldInsets];
119 | UIImage * foldLower = [MPAnimation renderImageFromView:forwards? self.sourceView : self.destinationView withRect:forwards? lowerRect : destLowerRect transparentInsets:foldInsets];
120 | if (isOddSize)
121 | {
122 | // ... except the bottom sleeve should also be 1 pixel taller (and moved up 1 to overlap 1 pixel with top sleeve)
123 | if (vertical)
124 | {
125 | lowerRect.size.height += 1;
126 | lowerRect.origin.y -= 1;
127 | destLowerRect.size.height += 1;
128 | destLowerRect.origin.y -= 1;
129 | }
130 | else
131 | {
132 | lowerRect.size.width += 1;
133 | lowerRect.origin.x -= 1;
134 | destLowerRect.size.width += 1;
135 | destLowerRect.origin.x -= 1;
136 | }
137 | }
138 | UIImage *slideUpper = [MPAnimation renderImageFromView:forwards? self.destinationView : self.sourceView withRect:forwards? destUpperRect : upperRect transparentInsets:slideInsets];
139 | UIImage *slideLower = [MPAnimation renderImageFromView:forwards? self.destinationView : self.sourceView withRect:forwards? destLowerRect : lowerRect transparentInsets:slideInsets];
140 |
141 | UIView *actingSource = [self sourceView]; // the view that is already part of the view hierarchy
142 | UIView *containerView = [actingSource superview];
143 | if (!containerView)
144 | {
145 | // in case of dismissal, it is actually the destination view since we had to add it
146 | // in order to get it to render correctly
147 | actingSource = [self destinationView];
148 | containerView = [actingSource superview];
149 | }
150 | [actingSource setHidden:YES];
151 |
152 | CATransform3D transform = CATransform3DIdentity;
153 | CALayer *topSleeve;
154 | CALayer *upperFold;
155 | CALayer *lowerFold;
156 | CALayer *bottomSleeve;
157 | CAGradientLayer *topSleeveShadow;
158 | CAGradientLayer *upperFoldShadow;
159 | CAGradientLayer *lowerFoldShadow;
160 | CAGradientLayer *bottomSleeveShadow;
161 | UIView *mainView;
162 | CGFloat width = vertical? bounds.size.width : bounds.size.height;
163 | CGFloat height = vertical? bounds.size.height/2 : bounds.size.width/2;
164 | CGFloat upperHeight = roundf(height * scale) / scale; // round heights to integer for odd height
165 | CGFloat lowerHeight = (height * 2) - upperHeight;
166 | CALayer *firstJointLayer;
167 | CALayer *secondJointLayer;
168 | CALayer *perspectiveLayer;
169 |
170 | // view to hold all our sublayers
171 | CGRect mainRect = [containerView convertRect:self.rect fromView:actingSource];
172 | CGPoint center = (CGPoint){CGRectGetMidX(mainRect), CGRectGetMidY(mainRect)};
173 | if ([containerView isKindOfClass:[UIWindow class]])
174 | mainRect = [actingSource convertRect:mainRect fromView:nil];
175 | mainView = [[UIView alloc] initWithFrame:mainRect];
176 | mainView.backgroundColor = [UIColor clearColor];
177 | mainView.transform = actingSource.transform;
178 | [containerView insertSubview:mainView atIndex:0];
179 | if ([containerView isKindOfClass:[UIWindow class]])
180 | {
181 | [mainView.layer setPosition:center];
182 | }
183 |
184 | // layer that covers the 2 folding panels in the middle
185 | perspectiveLayer = [CALayer layer];
186 | perspectiveLayer.frame = CGRectMake(0, 0, vertical? width : height * 2, vertical? height * 2 : width);
187 | [mainView.layer addSublayer:perspectiveLayer];
188 |
189 | // layer that encapsulates the join between the top sleeve (remains flat) and upper folding panel
190 | firstJointLayer = [CATransformLayer layer];
191 | firstJointLayer.frame = mainView.bounds;
192 | [perspectiveLayer addSublayer:firstJointLayer];
193 |
194 | // This remains flat, and is the upper half of the destination view when moving forwards
195 | // It slides down to meet the bottom sleeve in the center
196 | topSleeve = [CALayer layer];
197 | topSleeve.frame = CGRectMake(0, 0, vertical? width + slideInsetWidth : upperHeight, vertical? upperHeight : width + slideInsetWidth);
198 | topSleeve.anchorPoint = CGPointMake(vertical? 0.5 : 1, vertical? 1 : 0.5);
199 | topSleeve.position = CGPointMake(vertical? width/2 : 0, vertical? 0 : width/2);
200 | [topSleeve setContents:(id)[slideUpper CGImage]];
201 | [firstJointLayer addSublayer:topSleeve];
202 |
203 | // This piece folds away from user along top edge, and is the upper half of the source view when moving forwards
204 | upperFold = [CALayer layer];
205 | upperFold.frame = CGRectMake(0, 0, vertical? width + 2 : upperHeight, vertical? upperHeight : width + 2);
206 | upperFold.anchorPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
207 | upperFold.position = CGPointMake(vertical? width/2 : 0, vertical? 0 : width / 2);
208 | upperFold.contents = (id)[foldUpper CGImage];
209 | [firstJointLayer addSublayer:upperFold];
210 |
211 | // layer that encapsultates the join between the upper and lower folding panels (the V in the fold)
212 | secondJointLayer = [CATransformLayer layer];
213 | secondJointLayer.frame = mainView.bounds;
214 | secondJointLayer.frame = CGRectMake(0, 0, vertical? width : height * 2, vertical? height*2 : width);
215 | secondJointLayer.anchorPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
216 | secondJointLayer.position = CGPointMake(vertical? width/2 : upperHeight, vertical? upperHeight : width / 2);
217 | [firstJointLayer addSublayer:secondJointLayer];
218 |
219 | // This piece folds away from user along bottom edge, and is the lower half of the source view when moving forwards
220 | lowerFold = [CALayer layer];
221 | lowerFold.frame = CGRectMake(0, 0, vertical? width + 2 : lowerHeight, vertical? lowerHeight : width + 2);
222 | lowerFold.anchorPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
223 | lowerFold.position = CGPointMake(vertical? width/2 : 0, vertical? 0 : width / 2);
224 | lowerFold.contents = (id)[foldLower CGImage];
225 | [secondJointLayer addSublayer:lowerFold];
226 |
227 | // This remains flat, and is the lower half of the destination view when moving forwards
228 | // It slides up to meet the top sleeve in the center
229 | bottomSleeve = [CALayer layer];
230 | bottomSleeve.frame = CGRectMake(0, 0, vertical? width + slideInsetWidth : upperHeight, vertical? upperHeight : width + slideInsetWidth); // bottom sleeve for odd height is rounded up
231 | bottomSleeve.anchorPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
232 | bottomSleeve.position = CGPointMake(vertical? width/2 : lowerHeight, vertical? lowerHeight : width / 2);
233 | [bottomSleeve setContents:(id)[slideLower CGImage]];
234 | [secondJointLayer addSublayer:bottomSleeve];
235 |
236 | firstJointLayer.anchorPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
237 | firstJointLayer.position = CGPointMake(vertical? width/2 : 0, vertical? 0 : width / 2);
238 |
239 | // Shadow layers to add shadowing to the 2 folding panels
240 | upperFoldShadow = [CAGradientLayer layer];
241 | [upperFold addSublayer:upperFoldShadow];
242 | upperFoldShadow.frame = CGRectInset(upperFold.bounds, foldInsets.left, foldInsets.top);
243 | upperFoldShadow.colors = [NSArray arrayWithObjects:(id)[self foldShadowColor].CGColor, (id)[[UIColor clearColor] CGColor], nil];
244 | upperFoldShadow.startPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
245 | upperFoldShadow.endPoint = CGPointMake(vertical? 0.5 : 1, vertical? 1 : 0.5);
246 | upperFoldShadow.opacity = 0;
247 |
248 | lowerFoldShadow = [CAGradientLayer layer];
249 | [lowerFold addSublayer:lowerFoldShadow];
250 | lowerFoldShadow.frame = CGRectInset(lowerFold.bounds, foldInsets.left, foldInsets.top);
251 | // in non-cubic mode, don't set gradient end color as clear, but rather shadow color at alpha = 0.25
252 | // This keeps a visible crease between lower fold panel and bottom sleeve panel (no shadow)
253 | // (Not necessary in cubic mode because bottom panel will have its own gradient shadow to create contrast between the 2 panels)
254 | lowerFoldShadow.colors = [NSArray arrayWithObjects:(id)[self foldShadowColor].CGColor, (id)[(cubic? [UIColor clearColor] : [[self foldShadowColor] colorWithAlphaComponent:0.25]) CGColor], nil];
255 | lowerFoldShadow.startPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
256 | lowerFoldShadow.endPoint = CGPointMake(vertical? 0.5 : 1, vertical? 1 : 0.5);
257 | lowerFoldShadow.opacity = 0;
258 |
259 | if (cubic)
260 | {
261 | // add shadow layers to top and bottom sleeves as well
262 | topSleeveShadow = [CAGradientLayer layer];
263 | [topSleeve addSublayer:topSleeveShadow];
264 | topSleeveShadow.frame = CGRectInset(topSleeve.bounds, slideInsets.left, slideInsets.top);
265 | topSleeveShadow.colors = [NSArray arrayWithObjects:(id)[self foldShadowColor].CGColor, (id)[[UIColor clearColor] CGColor], nil];
266 | topSleeveShadow.startPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
267 | topSleeveShadow.endPoint = CGPointMake(vertical? 0.5 : 1, vertical? 1 : 0.5);
268 | topSleeveShadow.opacity = 0;
269 |
270 | bottomSleeveShadow = [CAGradientLayer layer];
271 | [bottomSleeve addSublayer:bottomSleeveShadow];
272 | bottomSleeveShadow.frame = CGRectInset(bottomSleeve.bounds, slideInsets.left, slideInsets.top);
273 | bottomSleeveShadow.colors = [NSArray arrayWithObjects:(id)[self foldShadowColor].CGColor, (id)[[UIColor clearColor] CGColor], nil];
274 | bottomSleeveShadow.startPoint = CGPointMake(vertical? 0.5 : 0, vertical? 0 : 0.5);
275 | bottomSleeveShadow.endPoint = CGPointMake(vertical? 0.5 : 1, vertical? 1 : 0.5);
276 | bottomSleeveShadow.opacity = 0;
277 | }
278 |
279 | NSUInteger frameCount = ceilf(self.duration * 60); // we want 60 FPS
280 |
281 | // Perspective is best proportional to the height of the pieces being folded away, rather than a fixed value
282 | // the larger the piece being folded, the more perspective distance (zDistance) is needed.
283 | // m34 = -1/zDistance
284 | if ([self m34] == INFINITY)
285 | transform.m34 = -1.0/(height * 4.6666667);
286 | else
287 | transform.m34 = [self m34];
288 | perspectiveLayer.sublayerTransform = transform;
289 |
290 | // Create a transaction (to group our animations with a single callback when done)
291 | [CATransaction begin];
292 | [CATransaction setValue:[NSNumber numberWithFloat:self.duration] forKey:kCATransactionAnimationDuration];
293 | [CATransaction setValue:[CAMediaTimingFunction functionWithName:[self timingCurveFunctionName]] forKey:kCATransactionAnimationTimingFunction];
294 | [CATransaction setCompletionBlock:^{
295 | [mainView removeFromSuperview];
296 | [self transitionDidComplete];
297 | if (completion)
298 | completion(YES); // execute the completion block that was passed in
299 | }];
300 |
301 | NSString *rotationKey = vertical? @"transform.rotation.x" : @"transform.rotation.y";
302 | double factor = (vertical? 1 : - 1) * M_PI / 180;
303 | // fold the first (top) joint away from us
304 | CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:rotationKey];
305 | [animation setFromValue:forwards? [NSNumber numberWithDouble:0] : [NSNumber numberWithDouble:-90*factor]];
306 | [animation setToValue:forwards? [NSNumber numberWithDouble:-90*factor] : [NSNumber numberWithDouble:0]];
307 | [animation setFillMode:kCAFillModeForwards];
308 | [animation setRemovedOnCompletion:NO];
309 | [firstJointLayer addAnimation:animation forKey:nil];
310 |
311 | // fold the second joint back towards us at twice the angle (since it's connected to the first fold we're folding away)
312 | animation = [CABasicAnimation animationWithKeyPath:rotationKey];
313 | [animation setFromValue:forwards? [NSNumber numberWithDouble:0] : [NSNumber numberWithDouble:180*factor]];
314 | [animation setToValue:forwards? [NSNumber numberWithDouble:180*factor] : [NSNumber numberWithDouble:0]];
315 | [animation setFillMode:kCAFillModeForwards];
316 | [animation setRemovedOnCompletion:NO];
317 | [secondJointLayer addAnimation:animation forKey:nil];
318 |
319 | if (cubic)
320 | {
321 | // for cubic animation, bottom and top-sleeves remain at fixed 90 degree angles to lower and upper folds, respectively
322 | [bottomSleeve setTransform:CATransform3DRotate([bottomSleeve transform], -90*factor, vertical? 1 : 0, vertical? 0 : 1, 0)];
323 | [topSleeve setTransform:CATransform3DRotate([topSleeve transform], 90*factor, vertical? 1 : 0, vertical? 0 : 1, 0)];
324 | }
325 | else
326 | {
327 | // fold the bottom sleeve (3rd joint) away from us, so that net result is it lays flat from user's perspective
328 | animation = [CABasicAnimation animationWithKeyPath:rotationKey];
329 | [animation setFromValue:forwards? [NSNumber numberWithDouble:0] : [NSNumber numberWithDouble:-90*factor]];
330 | [animation setToValue:forwards? [NSNumber numberWithDouble:-90*factor] : [NSNumber numberWithDouble:0]];
331 | [animation setFillMode:kCAFillModeForwards];
332 | [animation setRemovedOnCompletion:NO];
333 | [bottomSleeve addAnimation:animation forKey:nil];
334 |
335 | // fold top sleeve towards us, so that net result is it lays flat from user's perspective
336 | animation = [CABasicAnimation animationWithKeyPath:rotationKey];
337 | [animation setFromValue:forwards? [NSNumber numberWithDouble:0] : [NSNumber numberWithDouble:90*factor]];
338 | [animation setToValue:forwards? [NSNumber numberWithDouble:90*factor] : [NSNumber numberWithDouble:0]];
339 | [animation setFillMode:kCAFillModeForwards];
340 | [animation setRemovedOnCompletion:NO];
341 | [topSleeve addAnimation:animation forKey:nil];
342 | }
343 |
344 | // Build an array of keyframes for perspectiveLayer.bounds.size.height, and also for shadows
345 | NSMutableArray* arrayHeight = [NSMutableArray arrayWithCapacity:frameCount + 1];
346 | NSMutableArray* arrayShadow = [NSMutableArray arrayWithCapacity:frameCount + 1];
347 | NSMutableArray* arrayCubicShadow = cubic? [NSMutableArray arrayWithCapacity:frameCount + 1] : nil;
348 | CGFloat progress;
349 | CGFloat cosine, sine;
350 | CGFloat cosHeight;
351 | CGFloat shadowOpacity = [self foldShadowOpacity];
352 | for (int frame = 0; frame <= frameCount; frame++)
353 | {
354 | progress = (((float)frame) / frameCount);
355 | cosine = forwards? cos(mp_radians(90 * progress)) : sin(mp_radians(90 * progress));
356 | if ((forwards && frame == frameCount) || (!forwards && frame == 0))
357 | cosine = 0;
358 | cosHeight = cosine * 2 * height; // range from 2*height to 0 along a cosine curve
359 | [arrayHeight addObject:[NSNumber numberWithFloat:cosHeight]];
360 |
361 | // fold panel shadow intensity is inversely proportional to its height
362 | [arrayShadow addObject:[NSNumber numberWithFloat:((1-cosine) * shadowOpacity)]];
363 | if (cubic)
364 | {
365 | sine = forwards? sin(mp_radians(90 * progress)) : cos(mp_radians(90 * progress));
366 | if ((forwards && frame == 0) || (!forwards && frame == frameCount))
367 | sine = 0;
368 | // sleeve panel shadow intensity is inversely proportional to its height
369 | [arrayCubicShadow addObject:[NSNumber numberWithFloat:((1-sine) * shadowOpacity)]];
370 | }
371 | }
372 |
373 | // resize height of the 2 folding panels along a cosine curve. This is necessary to maintain the 2nd joint in the center
374 | // Since there's no built-in sine timing curve, we'll use CAKeyframeAnimation to achieve it
375 | CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:vertical? @"bounds.size.height" : @"bounds.size.width"];
376 | [keyAnimation setValues:[NSArray arrayWithArray:arrayHeight]];
377 | [keyAnimation setFillMode:kCAFillModeForwards];
378 | [keyAnimation setRemovedOnCompletion:NO];
379 | [perspectiveLayer addAnimation:keyAnimation forKey:nil];
380 |
381 | // Dim the 2 folding panels as they fold away from us
382 | // The gradients create a crease effect between adjacent panels
383 | keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
384 | [keyAnimation setValues:[NSArray arrayWithArray:arrayShadow]];
385 | [keyAnimation setFillMode:kCAFillModeForwards];
386 | [keyAnimation setRemovedOnCompletion:NO];
387 | [upperFoldShadow addAnimation:keyAnimation forKey:nil];
388 |
389 | keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
390 | [keyAnimation setValues:[NSArray arrayWithArray:arrayShadow]];
391 | [keyAnimation setFillMode:kCAFillModeForwards];
392 | [keyAnimation setRemovedOnCompletion:NO];
393 | [lowerFoldShadow addAnimation:keyAnimation forKey:nil];
394 |
395 | if (cubic)
396 | {
397 | keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
398 | [keyAnimation setValues:[NSArray arrayWithArray:arrayCubicShadow]];
399 | [keyAnimation setFillMode:kCAFillModeForwards];
400 | [keyAnimation setRemovedOnCompletion:NO];
401 | [topSleeveShadow addAnimation:keyAnimation forKey:nil];
402 |
403 | keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
404 | [keyAnimation setValues:[NSArray arrayWithArray:arrayCubicShadow]];
405 | [keyAnimation setFillMode:kCAFillModeForwards];
406 | [keyAnimation setRemovedOnCompletion:NO];
407 | [bottomSleeveShadow addAnimation:keyAnimation forKey:nil];
408 | }
409 |
410 | // Commit the transaction
411 | [CATransaction commit];
412 | }
413 |
414 | #pragma mark - Class methods
415 |
416 | + (void)transitionFromViewController:(UIViewController *)fromController toViewController:(UIViewController *)toController duration:(NSTimeInterval)duration style:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion
417 | {
418 | MPFoldTransition *foldTransition = [[MPFoldTransition alloc] initWithSourceView:fromController.view destinationView:toController.view duration:duration style:style completionAction:MPTransitionActionNone];
419 | [foldTransition perform:completion];
420 | }
421 |
422 | + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration style:(MPFoldStyle)style transitionAction:(MPTransitionAction)action completion:(void (^)(BOOL finished))completion
423 | {
424 | MPFoldTransition *foldTransition = [[MPFoldTransition alloc] initWithSourceView:fromView destinationView:toView duration:duration style:style completionAction:action];
425 | [foldTransition perform:completion];
426 | }
427 |
428 | + (void)presentViewController:(UIViewController *)viewControllerToPresent from:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion
429 | {
430 | MPFoldTransition *foldTransition = [[MPFoldTransition alloc] initWithSourceView:presentingController.view destinationView:viewControllerToPresent.view duration:duration style:style completionAction:MPTransitionActionNone];
431 |
432 | [foldTransition setPresentingController:presentingController];
433 | [foldTransition setPresentedController:viewControllerToPresent];
434 |
435 | [foldTransition perform:^(BOOL finished) {
436 | // under iPad for our fold transition, we need to be full screen modal (iPhone is always full screen modal)
437 | UIModalPresentationStyle oldStyle = [presentingController modalPresentationStyle];
438 | if (oldStyle != UIModalPresentationFullScreen && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
439 | [presentingController setModalPresentationStyle:UIModalPresentationFullScreen];
440 |
441 | [presentingController presentViewController:viewControllerToPresent animated:NO completion:^{
442 | // restore previous modal presentation style
443 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
444 | [presentingController setModalPresentationStyle:oldStyle];
445 | }];
446 |
447 | if (completion)
448 | completion(YES);
449 | }];
450 | }
451 |
452 | + (void)dismissViewControllerFromPresentingController:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion
453 | {
454 | UIViewController *src = [presentingController presentedViewController];
455 | if (!src)
456 | [NSException raise:@"Invalid Operation" format:@"dismissViewControllerFromPresentingController:direction:completion: can only be performed on a view controller with a presentedViewController."];
457 |
458 | UIViewController *dest = (UIViewController *)presentingController;
459 |
460 | // find out the presentation context for the presenting view controller
461 | while (YES)// (![src definesPresentationContext])
462 | {
463 | if (![dest parentViewController])
464 | break;
465 |
466 | dest = [dest parentViewController];
467 | }
468 |
469 | MPFoldTransition *foldTransition = [[MPFoldTransition alloc] initWithSourceView:src.view destinationView:dest.view duration:duration style:style completionAction:MPTransitionActionNone];
470 | [foldTransition setDismissing:YES];
471 | [foldTransition setPresentedController:src];
472 | [presentingController dismissViewControllerAnimated:NO completion:nil];
473 | [foldTransition perform:^(BOOL finished) {
474 | [dest.view setHidden:NO];
475 | if (completion)
476 | completion(YES);
477 | }];
478 | }
479 |
480 | @end
481 |
482 | #pragma mark - UIViewController(MPFoldTransition)
483 |
484 | @implementation UIViewController(MPFoldTransition)
485 |
486 | - (void)presentViewController:(UIViewController *)viewControllerToPresent foldStyle:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion
487 | {
488 | [MPFoldTransition presentViewController:viewControllerToPresent from:self duration:[MPFoldTransition defaultDuration] style:style completion:completion];
489 | }
490 |
491 | - (void)dismissViewControllerWithFoldStyle:(MPFoldStyle)style completion:(void (^)(BOOL finished))completion
492 | {
493 | [MPFoldTransition dismissViewControllerFromPresentingController:self duration:[MPFoldTransition defaultDuration] style:style completion:completion];
494 | }
495 |
496 | @end
497 |
498 | #pragma mark - UINavigationController(MPFoldTransition)
499 |
500 | @implementation UINavigationController(MPFoldTransition)
501 |
502 | //- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
503 | - (void)pushViewController:(UIViewController *)viewController foldStyle:(MPFoldStyle)style
504 | {
505 | [MPFoldTransition transitionFromViewController:[self visibleViewController]
506 | toViewController:viewController
507 | duration:[MPFoldTransition defaultDuration]
508 | style:style
509 | completion:^(BOOL finished) {
510 | [self pushViewController:viewController animated:NO];
511 | }
512 | ];
513 | }
514 |
515 | - (UIViewController *)popViewControllerWithFoldStyle:(MPFoldStyle)style
516 | {
517 | UIViewController *toController = [[self viewControllers] objectAtIndex:[[self viewControllers] count] - 2];
518 |
519 | [MPFoldTransition transitionFromViewController:[self visibleViewController]
520 | toViewController:toController
521 | duration:[MPFoldTransition defaultDuration]
522 | style:style
523 | completion:^(BOOL finished) {
524 | [self popViewControllerAnimated:NO];
525 | }
526 | ];
527 |
528 | return toController;
529 | }
530 |
531 | - (NSArray*)popToRootViewControllerWithFoldStyle:(MPFoldStyle)style
532 | {
533 | UIViewController* toController = [[self viewControllers] objectAtIndex:0]; //to rootViewController
534 |
535 | NSMutableArray* popped = [NSMutableArray array];
536 | for(int i = 1; i < [[self viewControllers] count]; i++)
537 | [popped addObject:[[self viewControllers] objectAtIndex:i]];
538 |
539 | [MPFoldTransition transitionFromViewController:[self visibleViewController]
540 | toViewController:toController
541 | duration:[MPFoldTransition defaultDuration]
542 | style:style
543 | completion:^(BOOL finished) {
544 | [self popToRootViewControllerAnimated:NO];
545 | }
546 | ];
547 |
548 | return popped;
549 | }
550 |
551 | @end
552 |
--------------------------------------------------------------------------------
/MPFoldTransition/MPFoldTransition/MPFlipTransition.m:
--------------------------------------------------------------------------------
1 | //
2 | // MPFlipTransition.m
3 | // MPTransition (v1.1.4)
4 | //
5 | // Created by Mark Pospesel on 5/15/12.
6 | // Copyright (c) 2012 Mark Pospesel. All rights reserved.
7 | //
8 |
9 | #define DEFAULT_COVERED_PAGE_SHADOW_OPACITY (1./3)
10 | #define DEFAULT_FLIPPING_PAGE_SHADOW_OPACITY 0.1
11 |
12 | #import "MPFlipTransition.h"
13 | #import "MPAnimation.h"
14 | #import
15 | #include
16 |
17 | static inline double mp_radians (double degrees) {return degrees * M_PI/180;}
18 |
19 | @interface MPFlipTransition()
20 |
21 | @property (assign, nonatomic, getter = wasDestinationViewShown) BOOL destinationViewShown;
22 | @property (assign, nonatomic, getter = wereLayersBuilt) BOOL layersBuilt;
23 | @property (strong, nonatomic) UIView *animationView;
24 | @property (strong, nonatomic) CALayer *layerFront;
25 | @property (strong, nonatomic) CALayer *layerFacing;
26 | @property (strong, nonatomic) CALayer *layerBack;
27 | @property (strong, nonatomic) CALayer *layerReveal;
28 | @property (strong, nonatomic) CAShapeLayer *revealLayerMask;
29 | @property (strong, nonatomic) CAGradientLayer *layerFrontShadow;
30 | @property (strong, nonatomic) CAGradientLayer *layerBackShadow;
31 | @property (strong, nonatomic) CALayer *layerFacingShadow;
32 | @property (strong, nonatomic) CALayer *layerRevealShadow;
33 | @property (assign, nonatomic) NSUInteger flipStage;
34 |
35 | @end
36 |
37 | @implementation MPFlipTransition
38 |
39 | #pragma mark - Properties
40 |
41 | @synthesize destinationViewShown = _destinationViewShown;
42 | @synthesize layersBuilt = _layersBuilt;
43 | @synthesize animationView = _animationView;
44 | @synthesize layerFront = _layerFront;
45 | @synthesize layerFacing = _layerFacing;
46 | @synthesize layerBack = _layerBack;
47 | @synthesize layerReveal = _layerReveal;
48 | @synthesize revealLayerMask = _revealLayerMask;
49 | @synthesize layerFrontShadow = _layerFrontShadow;
50 | @synthesize layerBackShadow = _layerBackShadow;
51 | @synthesize layerFacingShadow = _layerFacingShadow;
52 | @synthesize layerRevealShadow = _layerRevealShadow;
53 | @synthesize flipStage = _flipStage;
54 |
55 | @synthesize style = _style;
56 | @synthesize coveredPageShadowOpacity = _coveredPageShadowOpacity;
57 | @synthesize flippingPageShadowOpacity = _flippingPageShadowOpacity;
58 | @synthesize flipShadowColor = _flipShadowColor;
59 |
60 | #pragma mark - init
61 |
62 | - (id)initWithSourceView:(UIView *)sourceView destinationView:(UIView *)destinationView duration:(NSTimeInterval)duration style:(MPFlipStyle)style completionAction:(MPTransitionAction)action {
63 | self = [super initWithSourceView:sourceView destinationView:destinationView duration:duration timingCurve:UIViewAnimationCurveEaseInOut completionAction:action];
64 | if (self)
65 | {
66 | _style = style;
67 | _coveredPageShadowOpacity = DEFAULT_COVERED_PAGE_SHADOW_OPACITY;
68 | _flippingPageShadowOpacity = DEFAULT_FLIPPING_PAGE_SHADOW_OPACITY;
69 | _flipShadowColor = [UIColor blackColor];
70 | _layersBuilt = NO;
71 | _flipStage = 0;
72 | }
73 |
74 | return self;
75 | }
76 |
77 | #pragma mark - Instance methods
78 |
79 | // We split the animation into 2 parts, so don't ease out on the 1st half (we'll do that in 2nd half)
80 | - (NSString *)timingCurveFunctionNameFirstHalf
81 | {
82 | switch ([self timingCurve]) {
83 | case UIViewAnimationCurveEaseIn:
84 | case UIViewAnimationCurveEaseInOut:
85 | return kCAMediaTimingFunctionEaseIn;
86 |
87 | case UIViewAnimationCurveEaseOut:
88 | case UIViewAnimationCurveLinear:
89 | return kCAMediaTimingFunctionLinear;
90 | }
91 |
92 | return kCAMediaTimingFunctionEaseIn;
93 | }
94 |
95 | // We split the animation into 2 parts, so don't ease in on the 2nd half (we did that in the 1st half)
96 | - (NSString *)timingCurveFunctionNameSecondHalf
97 | {
98 | switch ([self timingCurve]) {
99 | case UIViewAnimationCurveEaseOut:
100 | case UIViewAnimationCurveEaseInOut:
101 | return kCAMediaTimingFunctionEaseOut;
102 |
103 | case UIViewAnimationCurveEaseIn:
104 | case UIViewAnimationCurveLinear:
105 | return kCAMediaTimingFunctionLinear;
106 | }
107 |
108 | return kCAMediaTimingFunctionEaseOut;
109 | }
110 |
111 | // switching between the 2 halves of the animation - between front and back sides of the page we're turning
112 | - (void)switchToStage:(int)stageIndex
113 | {
114 | // 0 = stage 1, 1 = stage 2
115 | [CATransaction begin];
116 | [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
117 |
118 | if (stageIndex == 0)
119 | {
120 | [self doFlip2:0];
121 | [self.animationView.layer insertSublayer:self.layerFacing above:self.layerReveal]; // re-order these 2 layers
122 | [self.animationView.layer insertSublayer:self.layerFront below:self.layerFacing];
123 | [self.layerReveal addSublayer:self.layerRevealShadow];
124 |
125 | [self.layerBack removeFromSuperlayer];
126 | [self.layerFacingShadow removeFromSuperlayer];
127 | }
128 | else
129 | {
130 | [self doFlip1:1];
131 | [self.animationView.layer insertSublayer:self.layerReveal above:self.layerFacing]; // re-order these 2 layers
132 | [self.animationView.layer insertSublayer:self.layerBack below:self.layerReveal];
133 | [self.layerFacing addSublayer:self.layerFacingShadow];
134 |
135 | [self.layerFront removeFromSuperlayer];
136 | [self.layerRevealShadow removeFromSuperlayer];
137 | }
138 |
139 | [CATransaction commit];
140 | }
141 |
142 | - (void)buildLayers
143 | {
144 | if ([self wereLayersBuilt])
145 | return;
146 |
147 | BOOL forwards = ([self style] & MPFlipStyleDirectionMask) != MPFlipStyleDirectionBackward;
148 | BOOL vertical = ([self style] & MPFlipStyleOrientationMask) == MPFlipStyleOrientationVertical;
149 | BOOL inward = ([self style] & MPFlipStylePerspectiveMask) == MPFlipStylePerspectiveReverse;
150 |
151 | CGRect bounds = [self calculateRect];
152 | CGFloat scale = [[UIScreen mainScreen] scale];
153 |
154 | // we inset the panels 1 point on each side with a transparent margin to antialiase the edges
155 | UIEdgeInsets insets = vertical? UIEdgeInsetsMake(0, 1, 0, 1) : UIEdgeInsetsMake(1, 0, 1, 0);
156 |
157 | CGRect upperRect = bounds;
158 | if (vertical)
159 | upperRect.size.height = bounds.size.height / 2;
160 | else
161 | upperRect.size.width = bounds.size.width / 2;
162 | CGRect lowerRect = upperRect;
163 | BOOL isOddSize = vertical? (upperRect.size.height != (roundf(upperRect.size.height * scale)/scale)) : (upperRect.size.width != (roundf(upperRect.size.width * scale) / scale));
164 | if (isOddSize)
165 | {
166 | // If view has an odd height, make the 2 panels of integer height with top panel 1 pixel taller (see below)
167 | if (vertical)
168 | {
169 | upperRect.size.height = (roundf(upperRect.size.height * scale)/scale);
170 | lowerRect.size.height = bounds.size.height - upperRect.size.height;
171 | }
172 | else
173 | {
174 | upperRect.size.width = (roundf(upperRect.size.width * scale)/scale);
175 | lowerRect.size.width = bounds.size.width - upperRect.size.width;
176 | }
177 | }
178 | if (vertical)
179 | lowerRect.origin.y += upperRect.size.height;
180 | else
181 | lowerRect.origin.x += upperRect.size.width;
182 |
183 | if (![self isDimissing])
184 | {
185 | if ([self presentedControllerIncludesStatusBarInFrame])
186 | self.destinationView.bounds = CGRectMake(0, 0, bounds.size.width + bounds.origin.x, bounds.size.height + bounds.origin.y);
187 | else
188 | self.destinationView.bounds = (CGRect){CGPointZero, bounds.size};
189 | }
190 |
191 | CGRect destUpperRect = CGRectOffset(upperRect, -upperRect.origin.x, -upperRect.origin.y);
192 | CGRect destLowerRect = CGRectOffset(lowerRect, -upperRect.origin.x, -upperRect.origin.y);
193 |
194 | if ([self isDimissing])
195 | {
196 | CGFloat x = self.destinationView.bounds.size.width - bounds.size.width;
197 | CGFloat y = self.destinationView.bounds.size.height - bounds.size.height;
198 | destUpperRect.origin.x += x;
199 | destLowerRect.origin.x += x;
200 | destUpperRect.origin.y += y;
201 | destLowerRect.origin.y += y;
202 | if (![self presentedControllerIncludesStatusBarInFrame])
203 | [self setRect:CGRectOffset([self rect], x, y)];
204 | }
205 | else if ([self presentedControllerIncludesStatusBarInFrame])
206 | {
207 | destUpperRect.origin.x += bounds.origin.x;
208 | destLowerRect.origin.x += bounds.origin.x;
209 | destUpperRect.origin.y += bounds.origin.y;
210 | destLowerRect.origin.y += bounds.origin.y;
211 | }
212 |
213 | // Create 4 images to represent 2 halves of the 2 views
214 |
215 | // The page flip animation is broken into 2 halves
216 | // 1. Flip old page up to vertical
217 | // 2. Flip new page from vertical down to flat
218 | // as we pass the halfway point of the animation, the "page" switches from old to new
219 |
220 | // front Page = the half of current view we are flipping during 1st half
221 | // facing Page = the other half of the current view (doesn't move, gets covered by back page during 2nd half)
222 | // back Page = the half of the next view that appears on the flipping page during 2nd half
223 | // reveal Page = the other half of the next view (doesn't move, gets revealed by front page during 1st half)
224 | UIImage *pageFrontImage = [MPAnimation renderImageFromView:self.sourceView withRect:forwards? lowerRect : upperRect transparentInsets:insets];
225 |
226 | UIView *actingSource = [self sourceView]; // the view that is already part of the view hierarchy
227 | UIView *containerView = [actingSource superview];
228 | if (!containerView)
229 | {
230 | // in case of dismissal, it is actually the destination view since we had to add it
231 | // in order to get it to render correctly
232 | actingSource = [self destinationView];
233 | containerView = [actingSource superview];
234 | }
235 |
236 | BOOL isDestinationViewAbove = YES;
237 | BOOL isModal = [containerView isKindOfClass:[UIWindow class]];
238 | BOOL drawFacing = NO, drawReveal = NO;
239 |
240 | switch (self.completionAction)
241 | {
242 | case MPTransitionActionAddRemove:
243 | if (!isModal)
244 | [self.destinationView setFrame:[self.sourceView frame]];
245 | [containerView addSubview:self.destinationView];
246 | break;
247 |
248 | case MPTransitionActionShowHide:
249 | [self.destinationView setHidden:NO];
250 | isDestinationViewAbove = [self.destinationView isAboveSiblingView:self.sourceView];
251 | break;
252 |
253 | case MPTransitionActionNone:
254 | if ([self.destinationView superview] == [self.sourceView superview])
255 | {
256 | isDestinationViewAbove = [self.destinationView isAboveSiblingView:self.sourceView];
257 | if ([self.destinationView isHidden])
258 | {
259 | [self.destinationView setHidden:NO];
260 | [self setDestinationViewShown:YES];
261 | }
262 | }
263 | else if (![self.sourceView superview])
264 | {
265 | drawFacing = YES;
266 | }
267 | else
268 | {
269 | drawReveal = YES;
270 | if ([self.destinationView isHidden])
271 | {
272 | [self.destinationView setHidden:NO];
273 | [self setDestinationViewShown:YES];
274 | }
275 | }
276 | break;
277 | }
278 |
279 | UIImage *pageFacingImage = drawFacing? [MPAnimation renderImageFromView:self.sourceView withRect:forwards? upperRect : lowerRect] : nil;
280 |
281 | UIImage *pageBackImage = [MPAnimation renderImageFromView:self.destinationView withRect:forwards? destUpperRect : destLowerRect transparentInsets:insets];
282 | UIImage *pageRevealImage = drawReveal? [MPAnimation renderImageFromView:self.destinationView withRect:forwards? destLowerRect : destUpperRect] : nil;
283 |
284 | CATransform3D transform = CATransform3DIdentity;
285 |
286 | CGFloat width = vertical? bounds.size.width : bounds.size.height;
287 | CGFloat height = vertical? bounds.size.height/2 : bounds.size.width/2;
288 | CGFloat upperHeight = roundf(height * scale) / scale; // round heights to integer for odd height
289 |
290 | // view to hold all our sublayers
291 | CGRect mainRect = [containerView convertRect:self.rect fromView:actingSource];
292 | CGPoint center = (CGPoint){CGRectGetMidX(mainRect), CGRectGetMidY(mainRect)};
293 | if (isModal)
294 | mainRect = [actingSource convertRect:mainRect fromView:nil];
295 | self.animationView = [[UIView alloc] initWithFrame:mainRect];
296 | self.animationView.backgroundColor = [UIColor clearColor];
297 | self.animationView.transform = actingSource.transform;
298 | self.animationView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
299 | [containerView addSubview:self.animationView];
300 | if (isModal)
301 | {
302 | [self.animationView.layer setPosition:center];
303 | }
304 |
305 | self.layerReveal = [CALayer layer];
306 | self.layerReveal.frame = (CGRect){CGPointZero, drawReveal? pageRevealImage.size : forwards? destLowerRect.size : destUpperRect.size};
307 | self.layerReveal.anchorPoint = CGPointMake(vertical? 0.5 : forwards? 0 : 1, vertical? forwards? 0 : 1 : 0.5);
308 | self.layerReveal.position = CGPointMake(vertical? width/2 : upperHeight, vertical? upperHeight : width/2);
309 | if (drawReveal)
310 | [self.layerReveal setContents:(id)[pageRevealImage CGImage]];
311 | [self.animationView.layer addSublayer:self.layerReveal];
312 |
313 | self.layerFacing = [CALayer layer];
314 | self.layerFacing.frame = (CGRect){CGPointZero, drawFacing? pageFacingImage.size : forwards? upperRect.size : lowerRect.size};
315 | self.layerFacing.anchorPoint = CGPointMake(vertical? 0.5 : forwards? 1 : 0, vertical? forwards? 1 : 0 : 0.5);
316 | self.layerFacing.position = CGPointMake(vertical? width/2 : upperHeight, vertical? upperHeight : width/2);
317 | if (drawFacing)
318 | [self.layerFacing setContents:(id)[pageFacingImage CGImage]];
319 | [self.animationView.layer addSublayer:self.layerFacing];
320 |
321 | self.revealLayerMask = [CAShapeLayer layer];
322 | CGRect maskRect = (forwards == isDestinationViewAbove)? destLowerRect : destUpperRect;
323 | self.revealLayerMask.path = [[UIBezierPath bezierPathWithRect:maskRect] CGPath];
324 | UIView *viewToMask = isDestinationViewAbove? self.destinationView : self.sourceView;
325 | [viewToMask.layer setMask:self.revealLayerMask];
326 |
327 | self.layerFront = [CALayer layer];
328 | self.layerFront.frame = (CGRect){CGPointZero, pageFrontImage.size};
329 | self.layerFront.anchorPoint = CGPointMake(vertical? 0.5 : forwards? 0 : 1, vertical? forwards? 0 : 1 : 0.5);
330 | self.layerFront.position = CGPointMake(vertical? width/2 : upperHeight, vertical? upperHeight : width/2);
331 | [self.layerFront setContents:(id)[pageFrontImage CGImage]];
332 | [self.animationView.layer addSublayer:self.layerFront];
333 |
334 | self.layerBack = [CALayer layer];
335 | self.layerBack.frame = (CGRect){CGPointZero, pageBackImage.size};
336 | self.layerBack.anchorPoint = CGPointMake(vertical? 0.5 : forwards? 1 : 0, vertical? forwards? 1 : 0 : 0.5);
337 | self.layerBack.position = CGPointMake(vertical? width/2 : upperHeight, vertical? upperHeight : width/2);
338 | [self.layerBack setContents:(id)[pageBackImage CGImage]];
339 |
340 | // Create shadow layers
341 | self.layerFrontShadow = [CAGradientLayer layer];
342 | [self.layerFront addSublayer:self.layerFrontShadow];
343 | self.layerFrontShadow.frame = CGRectInset(self.layerFront.bounds, insets.left, insets.top);
344 | self.layerFrontShadow.opacity = 0.0;
345 | if (forwards)
346 | self.layerFrontShadow.colors = [NSArray arrayWithObjects:(id)[[[self flipShadowColor] colorWithAlphaComponent:0.5] CGColor], (id)[self flipShadowColor].CGColor, (id)[[UIColor clearColor] CGColor], nil];
347 | else
348 | self.layerFrontShadow.colors = [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor], (id)[self flipShadowColor].CGColor, (id)[[[self flipShadowColor] colorWithAlphaComponent:0.5] CGColor], nil];
349 | self.layerFrontShadow.startPoint = CGPointMake(vertical? 0.5 : forwards? 0 : 0.5, vertical? forwards? 0 : 0.5 : 0.5);
350 | self.layerFrontShadow.endPoint = CGPointMake(vertical? 0.5 : forwards? 0.5 : 1, vertical? forwards? 0.5 : 1 : 0.5);
351 | self.layerFrontShadow.locations = [NSArray arrayWithObjects:[NSNumber numberWithDouble:0], [NSNumber numberWithDouble:forwards? 0.1 : 0.9], [NSNumber numberWithDouble:1], nil];
352 |
353 | self.layerBackShadow = [CAGradientLayer layer];
354 | [self.layerBack addSublayer:self.layerBackShadow];
355 | self.layerBackShadow.frame = CGRectInset(self.layerBack.bounds, insets.left, insets.top);
356 | self.layerBackShadow.opacity = [self flippingPageShadowOpacity];
357 | if (forwards)
358 | self.layerBackShadow.colors = [NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor], (id)[self flipShadowColor].CGColor, (id)[[[self flipShadowColor] colorWithAlphaComponent:0.5] CGColor], nil];
359 | else
360 | self.layerBackShadow.colors = [NSArray arrayWithObjects:(id)[[[self flipShadowColor] colorWithAlphaComponent:0.5] CGColor], (id)[self flipShadowColor].CGColor, (id)[[UIColor clearColor] CGColor], nil];
361 | self.layerBackShadow.startPoint = CGPointMake(vertical? 0.5 : forwards? 0.5 : 0, vertical? forwards? 0.5 : 0 : 0.5);
362 | self.layerBackShadow.endPoint = CGPointMake(vertical? 0.5 : forwards? 1 : 0.5, vertical? forwards? 1 : 0.5 : 0.5);
363 | self.layerBackShadow.locations = [NSArray arrayWithObjects:[NSNumber numberWithDouble:0], [NSNumber numberWithDouble:forwards? 0.9 : 0.1], [NSNumber numberWithDouble:1], nil];
364 |
365 | if (!inward)
366 | {
367 | self.layerRevealShadow = [CALayer layer];
368 | [self.layerReveal addSublayer:self.layerRevealShadow];
369 | self.layerRevealShadow.frame = self.layerReveal.bounds;
370 | self.layerRevealShadow.backgroundColor = [self flipShadowColor].CGColor;
371 | self.layerRevealShadow.opacity = [self coveredPageShadowOpacity];
372 |
373 | self.layerFacingShadow = [CALayer layer];
374 | //[self.layerFacing addSublayer:self.layerFacingShadow]; // add later
375 | self.layerFacingShadow.frame = self.layerFacing.bounds;
376 | self.layerFacingShadow.backgroundColor = [self flipShadowColor].CGColor;
377 | self.layerFacingShadow.opacity = 0.0;
378 | }
379 |
380 | // Perspective is best proportional to the height of the pieces being folded away, rather than a fixed value
381 | // the larger the piece being folded, the more perspective distance (zDistance) is needed.
382 | // m34 = -1/zDistance
383 | if ([self m34] == INFINITY)
384 | transform.m34 = -1.0/(height * 4.6666667);
385 | else
386 | transform.m34 = [self m34];
387 | if (inward)
388 | transform.m34 = -transform.m34; // flip perspective around
389 | self.animationView.layer.sublayerTransform = transform;
390 |
391 | [self setLayersBuilt:YES];
392 | }
393 |
394 | - (void)cleanupLayers
395 | {
396 | // cleanup
397 | if (![self wereLayersBuilt])
398 | return;
399 |
400 | [self.animationView removeFromSuperview];
401 |
402 | [CATransaction begin];
403 | [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
404 | [self.revealLayerMask removeFromSuperlayer]; // don't animate
405 | [CATransaction commit];
406 |
407 | self.animationView = nil;
408 | self.layerFront = nil;
409 | self.layerBack = nil;
410 | self.layerFacing = nil;
411 | self.layerReveal = nil;
412 | self.layerFrontShadow = nil;
413 | self.layerBackShadow = nil;
414 | self.layerFacingShadow = nil;
415 | self.layerRevealShadow = nil;
416 | self.revealLayerMask = nil;
417 |
418 | [self setLayersBuilt:NO];
419 | }
420 |
421 | - (void)perform:(void (^)(BOOL finished))completion
422 | {
423 | [self buildLayers];
424 | [self doFlip2:0]; // set back page to vertical
425 | [self animateFlip1:NO fromProgress:0 withCompletion:completion];
426 | }
427 |
428 | - (void)animateFlip1:(BOOL)isFallingBack fromProgress:(CGFloat)fromProgress withCompletion:(void (^)(BOOL finished))completion
429 | {
430 | BOOL forwards = ([self style] & MPFlipStyleDirectionMask) != MPFlipStyleDirectionBackward;
431 | BOOL vertical = ([self style] & MPFlipStyleOrientationMask) == MPFlipStyleOrientationVertical;
432 | BOOL inward = ([self style] & MPFlipStylePerspectiveMask) == MPFlipStylePerspectiveReverse;
433 |
434 | // 2-stage animation
435 | CALayer *layer = isFallingBack? self.layerBack : self.layerFront;
436 | CALayer *flippingShadow = isFallingBack? self.layerBackShadow : self.layerFrontShadow;
437 | CALayer *coveredShadow = isFallingBack? self.layerFacingShadow : self.layerRevealShadow;
438 |
439 | if (isFallingBack)
440 | fromProgress = 1 - fromProgress;
441 | CGFloat toProgress = 1;
442 |
443 | // Figure out how many frames we want
444 | CGFloat duration = (self.duration / 2) * (toProgress - fromProgress);
445 | NSUInteger frameCount = ceilf(duration * 60); // Let's shoot for 60 FPS to ensure proper sine curve approximation
446 |
447 | NSString *rotationKey = vertical? @"transform.rotation.x" : @"transform.rotation.y";
448 | double factor = (isFallingBack? -1 : 1) * (forwards? -1 : 1) * (vertical? -1 : 1) * M_PI / 180;
449 | CGFloat coveredPageShadowOpacity = [self coveredPageShadowOpacity];
450 |
451 | // Create a transaction
452 | [CATransaction begin];
453 | [CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration];
454 | [CATransaction setValue:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn] forKey:kCATransactionAnimationTimingFunction];
455 | [CATransaction setCompletionBlock:^{
456 | // 2nd half of animation, once 1st half completes
457 | [self setFlipStage:isFallingBack? 0 : 1];
458 | [self switchToStage:isFallingBack? 0 : 1];
459 |
460 | [self animateFlip2:isFallingBack fromProgress:isFallingBack? 1 : 0 withCompletion:completion];
461 | }];
462 |
463 | // First Half of Animation
464 |
465 | // Flip front page from flat up to vertical
466 | CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:rotationKey];
467 | [animation setFromValue:[NSNumber numberWithDouble:90 * factor * fromProgress]];
468 | [animation setToValue:[NSNumber numberWithDouble:90*factor]];
469 | [animation setFillMode:kCAFillModeForwards];
470 | [animation setRemovedOnCompletion:NO];
471 | [layer addAnimation:animation forKey:nil];
472 | [layer setTransform:CATransform3DMakeRotation(90*factor, vertical? 1 : 0, vertical? 0 : 1, 0)];
473 |
474 | // Shadows
475 |
476 | // darken front page just slightly as we flip (just to give it a crease where it touches facing page)
477 | animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
478 | [animation setFromValue:[NSNumber numberWithDouble:[self flippingPageShadowOpacity] * fromProgress]];
479 | [animation setToValue:[NSNumber numberWithDouble:[self flippingPageShadowOpacity]]];
480 | [animation setFillMode:kCAFillModeForwards];
481 | [animation setRemovedOnCompletion:NO];
482 | [flippingShadow addAnimation:animation forKey:nil];
483 | [flippingShadow setOpacity:[self flippingPageShadowOpacity]];
484 |
485 | if (!inward)
486 | {
487 | // lighten the page that is revealed by front page flipping up (along a cosine curve)
488 | // TODO: consider FROM value
489 | NSMutableArray* arrayOpacity = [NSMutableArray arrayWithCapacity:frameCount + 1];
490 | CGFloat progress;
491 | CGFloat cosOpacity;
492 | for (int frame = 0; frame <= frameCount; frame++)
493 | {
494 | progress = fromProgress + (toProgress - fromProgress) * ((float)frame) / frameCount;
495 | cosOpacity = cos(mp_radians(90 * progress)) * coveredPageShadowOpacity;
496 | if (frame == frameCount)
497 | cosOpacity = 0;
498 | [arrayOpacity addObject:[NSNumber numberWithFloat:cosOpacity]];
499 | }
500 |
501 | CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
502 | [keyAnimation setValues:[NSArray arrayWithArray:arrayOpacity]];
503 | [keyAnimation setFillMode:kCAFillModeForwards];
504 | [keyAnimation setRemovedOnCompletion:NO];
505 | [coveredShadow addAnimation:keyAnimation forKey:nil];
506 | [coveredShadow setOpacity:[[arrayOpacity lastObject] floatValue]];
507 | }
508 |
509 | // Commit the transaction for 1st half
510 | [CATransaction commit];
511 | }
512 |
513 | - (void)animateFlip2:(BOOL)isFallingBack fromProgress:(CGFloat)fromProgress withCompletion:(void (^)(BOOL finished))completion
514 | {
515 | // Second half of animation
516 | BOOL forwards = ([self style] & MPFlipStyleDirectionMask) != MPFlipStyleDirectionBackward;
517 | BOOL vertical = ([self style] & MPFlipStyleOrientationMask) == MPFlipStyleOrientationVertical;
518 | BOOL inward = ([self style] & MPFlipStylePerspectiveMask) == MPFlipStylePerspectiveReverse;
519 |
520 | // 1-stage animation
521 | CALayer *layer = isFallingBack? self.layerFront : self.layerBack;
522 | CALayer *flippingShadow = isFallingBack? self.layerFrontShadow : self.layerBackShadow;
523 | CALayer *coveredShadow = isFallingBack? self.layerRevealShadow : self.layerFacingShadow;
524 |
525 | NSUInteger frameCount = ceilf((self.duration / 2) * 60); // Let's shoot for 60 FPS to ensure proper sine curve approximation
526 |
527 | NSString *rotationKey = vertical? @"transform.rotation.x" : @"transform.rotation.y";
528 | double factor = (isFallingBack? -1 : 1) * (forwards? -1 : 1) * (vertical? -1 : 1) * M_PI / 180;
529 | CGFloat coveredPageShadowOpacity = [self coveredPageShadowOpacity];
530 |
531 | if (isFallingBack)
532 | fromProgress = 1 - fromProgress;
533 | CGFloat toProgress = 1;
534 |
535 | // Create a transaction
536 | [CATransaction begin];
537 | [CATransaction setValue:[NSNumber numberWithFloat:self.duration/2] forKey:kCATransactionAnimationDuration];
538 | [CATransaction setValue:[CAMediaTimingFunction functionWithName:[self timingCurveFunctionNameSecondHalf]] forKey:kCATransactionAnimationTimingFunction];
539 | [CATransaction setCompletionBlock:^{
540 | // This is the final completion block, when 2nd half of animation finishes
541 | [self cleanupLayers];
542 | [self transitionDidComplete];
543 |
544 | if (completion)
545 | completion(YES); // execute the completion block that was passed in
546 | }];
547 |
548 | // Flip back page from vertical down to flat
549 | CABasicAnimation* animation2 = [CABasicAnimation animationWithKeyPath:rotationKey];
550 | [animation2 setFromValue:[NSNumber numberWithDouble:-90*factor*(1-fromProgress)]];
551 | [animation2 setToValue:[NSNumber numberWithDouble:0]];
552 | [animation2 setFillMode:kCAFillModeForwards];
553 | [animation2 setRemovedOnCompletion:NO];
554 | [layer addAnimation:animation2 forKey:nil];
555 | [layer setTransform:CATransform3DIdentity];
556 |
557 | // Shadows
558 |
559 | // Lighten back page just slightly as we flip (just to give it a crease where it touches reveal page)
560 | animation2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
561 | [animation2 setFromValue:[NSNumber numberWithDouble:[self flippingPageShadowOpacity] * (1-fromProgress)]];
562 | [animation2 setToValue:[NSNumber numberWithDouble:0]];
563 | [animation2 setFillMode:kCAFillModeForwards];
564 | [animation2 setRemovedOnCompletion:NO];
565 | [flippingShadow addAnimation:animation2 forKey:nil];
566 | [flippingShadow setOpacity:0];
567 |
568 | if (!inward)
569 | {
570 | // Darken facing page as it gets covered by back page flipping down (along a sine curve)
571 | NSMutableArray* arrayOpacity = [NSMutableArray arrayWithCapacity:frameCount + 1];
572 | CGFloat progress;
573 | CGFloat sinOpacity;
574 | for (int frame = 0; frame <= frameCount; frame++)
575 | {
576 | progress = fromProgress + (toProgress - fromProgress) * ((float)frame) / frameCount;
577 | sinOpacity = (sin(mp_radians(90 * progress))* coveredPageShadowOpacity);
578 | if (frame == 0)
579 | sinOpacity = 0;
580 | [arrayOpacity addObject:[NSNumber numberWithFloat:sinOpacity]];
581 | }
582 |
583 | CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
584 | [keyAnimation setValues:[NSArray arrayWithArray:arrayOpacity]];
585 | [keyAnimation setFillMode:kCAFillModeForwards];
586 | [keyAnimation setRemovedOnCompletion:NO];
587 | [coveredShadow addAnimation:keyAnimation forKey:nil];
588 | [coveredShadow setOpacity:[[arrayOpacity lastObject] floatValue]];
589 | }
590 |
591 | // Commit the transaction for 2nd half
592 | [CATransaction commit];
593 | }
594 |
595 | // set view to any position within the 1st half of the animation
596 | // progress ranges from 0 (start) to 1 (complete)
597 | - (void)doFlip1:(CGFloat)progress
598 | {
599 | [CATransaction begin];
600 | [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
601 |
602 | if (progress < 0)
603 | progress = 0;
604 | else if (progress > 1)
605 | progress = 1;
606 |
607 | [self.layerFront setTransform:[self flipTransform1:progress]];
608 | [self.layerFrontShadow setOpacity:[self flippingPageShadowOpacity] * progress];
609 | CGFloat cosOpacity = cos(mp_radians(90 * progress)) * [self coveredPageShadowOpacity];
610 | [self.layerRevealShadow setOpacity:cosOpacity];
611 |
612 | [CATransaction commit];
613 | }
614 |
615 | // set view to any position within the 2nd half of the animation
616 | // progress ranges from 0 (start) to 1 (complete)
617 | - (void)doFlip2:(CGFloat)progress
618 | {
619 | [CATransaction begin];
620 | [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
621 |
622 | if (progress < 0)
623 | progress = 0;
624 | else if (progress > 1)
625 | progress = 1;
626 |
627 | [self.layerBack setTransform:[self flipTransform2:progress]];
628 | [self.layerBackShadow setOpacity:[self flippingPageShadowOpacity] * (1- progress)];
629 | CGFloat sinOpacity = sin(mp_radians(90 * progress)) * [self coveredPageShadowOpacity];
630 | [self.layerFacingShadow setOpacity:sinOpacity];
631 |
632 | [CATransaction commit];
633 | }
634 |
635 | // fetch the flipping page transform for any position within the 1st half of the animation
636 | // progress ranges from 0 (start) to 1 (complete)
637 | - (CATransform3D)flipTransform1:(CGFloat)progress
638 | {
639 | CATransform3D tHalf1 = CATransform3DIdentity;
640 |
641 | // rotate away from viewer
642 | BOOL forwards = ([self style] & MPFlipStyleDirectionMask) != MPFlipStyleDirectionBackward;
643 | BOOL vertical = ([self style] & MPFlipStyleOrientationMask) == MPFlipStyleOrientationVertical;
644 | tHalf1 = CATransform3DRotate(tHalf1, mp_radians(90 * progress * (forwards? -1 : 1)), vertical? -1 : 0, vertical? 0 : 1, 0);
645 |
646 | return tHalf1;
647 | }
648 |
649 | // fetch the flipping page transform for any position within the 2nd half of the animation
650 | // progress ranges from 0 (start) to 1 (complete)
651 | - (CATransform3D)flipTransform2:(CGFloat)progress
652 | {
653 | CATransform3D tHalf2 = CATransform3DIdentity;
654 |
655 | // rotate away from viewer
656 | BOOL forwards = ([self style] & MPFlipStyleDirectionMask) != MPFlipStyleDirectionBackward;
657 | BOOL vertical = ([self style] & MPFlipStyleOrientationMask) == MPFlipStyleOrientationVertical;
658 | tHalf2 = CATransform3DRotate(tHalf2, mp_radians(90 * (1 - progress)) * (forwards? 1 : -1), vertical? -1 : 0, vertical? 0 : 1, 0);
659 |
660 | return tHalf2;
661 | }
662 |
663 | - (void)transitionDidComplete
664 | {
665 | switch (self.completionAction) {
666 | case MPTransitionActionAddRemove:
667 | [self.sourceView removeFromSuperview];
668 | break;
669 |
670 | case MPTransitionActionShowHide:
671 | [self.sourceView setHidden:YES];
672 | break;
673 |
674 | case MPTransitionActionNone:
675 | // undo whatever actions we took during animation
676 | if ([self wasDestinationViewShown])
677 | [self.destinationView setHidden:YES];
678 | break;
679 | }
680 | }
681 |
682 | #pragma mark - Class methods
683 |
684 | + (void)transitionFromViewController:(UIViewController *)fromController toViewController:(UIViewController *)toController duration:(NSTimeInterval)duration style:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion
685 | {
686 | MPFlipTransition *flipTransition = [[MPFlipTransition alloc] initWithSourceView:fromController.view destinationView:toController.view duration:duration style:style completionAction:MPTransitionActionNone];
687 | [flipTransition perform:completion];
688 | }
689 |
690 | + (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration style:(MPFlipStyle)style transitionAction:(MPTransitionAction)action completion:(void (^)(BOOL finished))completion
691 | {
692 | MPFlipTransition *flipTransition = [[MPFlipTransition alloc] initWithSourceView:fromView destinationView:toView duration:duration style:style completionAction:action];
693 | [flipTransition perform:completion];
694 | }
695 |
696 | + (void)presentViewController:(UIViewController *)viewControllerToPresent from:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion
697 | {
698 | MPFlipTransition *flipTransition = [[MPFlipTransition alloc] initWithSourceView:presentingController.view destinationView:viewControllerToPresent.view duration:duration style:style completionAction:MPTransitionActionNone];
699 |
700 | [flipTransition setPresentingController:presentingController];
701 | [flipTransition setPresentedController:viewControllerToPresent];
702 |
703 | [flipTransition perform:^(BOOL finished) {
704 | // under iPad for our fold transition, we need to be full screen modal (iPhone is always full screen modal)
705 | UIModalPresentationStyle oldStyle = [presentingController modalPresentationStyle];
706 | if (oldStyle != UIModalPresentationFullScreen && [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
707 | [presentingController setModalPresentationStyle:UIModalPresentationFullScreen];
708 |
709 | [presentingController presentViewController:viewControllerToPresent animated:NO completion:^{
710 | // restore previous modal presentation style
711 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
712 | [presentingController setModalPresentationStyle:oldStyle];
713 | }];
714 |
715 | if (completion)
716 | completion(YES);
717 | }];
718 | }
719 |
720 | + (void)dismissViewControllerFromPresentingController:(UIViewController *)presentingController duration:(NSTimeInterval)duration style:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion
721 | {
722 | UIViewController *src = [presentingController presentedViewController];
723 | if (!src)
724 | [NSException raise:@"Invalid Operation" format:@"dismissViewControllerFromPresentingController:direction:completion: can only be performed on a view controller with a presentedViewController."];
725 |
726 | UIViewController *dest = (UIViewController *)presentingController;
727 |
728 | // find out the presentation context for the presenting view controller
729 | while (YES)// (![src definesPresentationContext])
730 | {
731 | if (![dest parentViewController])
732 | break;
733 |
734 | dest = [dest parentViewController];
735 | }
736 |
737 | MPFlipTransition *flipTransition = [[MPFlipTransition alloc] initWithSourceView:src.view destinationView:dest.view duration:duration style:style completionAction:MPTransitionActionNone];
738 | [flipTransition setDismissing:YES];
739 | [flipTransition setPresentedController:src];
740 | [presentingController dismissViewControllerAnimated:NO completion:nil];
741 | [flipTransition perform:^(BOOL finished) {
742 | [dest.view setHidden:NO];
743 | if (completion)
744 | completion(YES);
745 | }];
746 | }
747 |
748 | @end
749 |
750 | #pragma mark - UIViewController(MPFlipTransition)
751 |
752 | @implementation UIViewController(MPFlipTransition)
753 |
754 | - (void)presentViewController:(UIViewController *)viewControllerToPresent flipStyle:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion
755 | {
756 | [MPFlipTransition presentViewController:viewControllerToPresent from:self duration:[MPFlipTransition defaultDuration] style:style completion:completion];
757 | }
758 |
759 | - (void)dismissViewControllerWithFlipStyle:(MPFlipStyle)style completion:(void (^)(BOOL finished))completion
760 | {
761 | [MPFlipTransition dismissViewControllerFromPresentingController:self duration:[MPFlipTransition defaultDuration] style:style completion:completion];
762 | }
763 |
764 | @end
765 |
766 | #pragma mark - UINavigationController(MPFlipTransition)
767 |
768 | @implementation UINavigationController(MPFlipTransition)
769 |
770 | //- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
771 | - (void)pushViewController:(UIViewController *)viewController flipStyle:(MPFlipStyle)style
772 | {
773 | [MPFlipTransition transitionFromViewController:[self visibleViewController]
774 | toViewController:viewController
775 | duration:[MPFlipTransition defaultDuration]
776 | style:style
777 | completion:^(BOOL finished) {
778 | [self pushViewController:viewController animated:NO];
779 | }
780 | ];
781 | }
782 |
783 | - (UIViewController *)popViewControllerWithFlipStyle:(MPFlipStyle)style
784 | {
785 | UIViewController *toController = [[self viewControllers] objectAtIndex:[[self viewControllers] count] - 2];
786 |
787 | [MPFlipTransition transitionFromViewController:[self visibleViewController]
788 | toViewController:toController
789 | duration:[MPFlipTransition defaultDuration]
790 | style:style
791 | completion:^(BOOL finished) {
792 | [self popViewControllerAnimated:NO];
793 | }
794 | ];
795 |
796 | return toController;
797 | }
798 |
799 | - (NSArray*)popToRootViewControllerWithFlipStyle:(MPFlipStyle)style
800 | {
801 | UIViewController* toController = [[self viewControllers] objectAtIndex:0]; //to rootViewController
802 |
803 | NSMutableArray* popped = [NSMutableArray array];
804 | for(int i = 1; i < [[self viewControllers] count]; i++)
805 | [popped addObject:[[self viewControllers] objectAtIndex:i]];
806 |
807 | [MPFlipTransition transitionFromViewController:[self visibleViewController]
808 | toViewController:toController
809 | duration:[MPFlipTransition defaultDuration]
810 | style:style
811 | completion:^(BOOL finished) {
812 | [self popToRootViewControllerAnimated:NO];
813 | }
814 | ];
815 |
816 | return popped;
817 | }
818 |
819 | @end
820 |
--------------------------------------------------------------------------------
/MPFoldTransition/en.lproj/MainStoryboard_iPhone.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
35 |
42 |
49 |
64 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
432 |
433 |
434 |
435 |
436 |
443 |
450 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
483 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
--------------------------------------------------------------------------------