├── 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 | ![iPhone Fold](http://markpospesel.files.wordpress.com/2012/05/iphone-fold2.png) 9 | MPFlipTransition is a set of classes to add page-flipping transitions to iOS 5 projects. 10 | ![iPhone Flip](http://markpospesel.files.wordpress.com/2012/05/iphone-flip-3.png) 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 | ![Fold vs. Unfold](http://markpospesel.files.wordpress.com/2012/05/fold-vs-unfold2.png) 27 | * __Mode:__ Normal vs. Cubic 28 | ![Normal vs. Cubic](http://markpospesel.files.wordpress.com/2012/05/normal-vs-cubic2.png) 29 | * __Orientation:__ Vertical vs. Horizontal 30 | ![Vertical vs. Horizontal](http://markpospesel.files.wordpress.com/2012/05/vertical-vs-horizontal2.png) 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 | ![Forward vs. Backward](http://markpospesel.files.wordpress.com/2012/05/forward-vs-backward-3.png) 37 | * __Orientation:__ Horizontal vs. Vertical 38 | ![Horizontal vs. Vertical](http://markpospesel.files.wordpress.com/2012/05/horizontal-vs-vertical-3.png) 39 | * __Perspective:__ Normal vs. Reverse 40 | ![Normal vs. Reverse](http://markpospesel.files.wordpress.com/2012/05/normal-vs-reverse-3.png) 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 | --------------------------------------------------------------------------------