Created
December 17, 2014 15:40
-
-
Save pablocarrillo/ea09635abf23137db859 to your computer and use it in GitHub Desktop.
Modal transition animators, to make a lateral animation from the left. based on: https://github.com/pronebird/CustomModalTransition/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ModalTransitionAnimator.h | |
// CustomModalTransition | |
// | |
// Created by pronebird on 29/05/14. | |
// Copyright (c) 2014 codeispoetry.ru. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface ModalTransitionAnimator : NSObject<UIViewControllerAnimatedTransitioning> | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ModalTransitionAnimator.m | |
// CustomModalTransition | |
// | |
// Created by pronebird on 29/05/14. | |
// Copyright (c) 2014 codeispoetry.ru. All rights reserved. | |
// | |
#import "ModalTransitionAnimator.h" | |
@implementation ModalTransitionAnimator | |
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { | |
return 0.5; | |
} | |
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { | |
UIViewController* destination = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; | |
if([destination isBeingPresented]) { | |
[self animatePresentation:transitionContext]; | |
} else { | |
[self animateDismissal:transitionContext]; | |
} | |
} | |
// | |
// Calculate a final frame for presenting controller according to interface orientation | |
// Presenting controller should always slide down and its top should coincide with the bottom of screen | |
// | |
- (CGRect)presentingControllerFrameWithContext:(id<UIViewControllerContextTransitioning>)transitionContext { | |
CGRect frame = transitionContext.containerView.bounds; | |
if(floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) // iOS 8+ | |
{ | |
// | |
// On iOS 8, UIKit handles rotation using transform matrix | |
// Therefore we should always return a frame for portrait mode | |
// | |
return CGRectMake(0, CGRectGetHeight(frame), CGRectGetWidth(frame), CGRectGetHeight(frame)); | |
} | |
else | |
{ | |
// | |
// On iOS 7, UIKit does not handle rotation | |
// To make sure our view is moving in the right direction (always down) we should | |
// fix the frame accoding to interface orientation. | |
// | |
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; | |
switch (orientation) { | |
case UIInterfaceOrientationLandscapeLeft: | |
return CGRectMake(CGRectGetWidth(frame), 0, CGRectGetWidth(frame), CGRectGetHeight(frame)); | |
break; | |
case UIInterfaceOrientationLandscapeRight: | |
return CGRectMake(-CGRectGetWidth(frame), 0, CGRectGetWidth(frame), CGRectGetHeight(frame)); | |
break; | |
case UIInterfaceOrientationPortraitUpsideDown: | |
return CGRectMake(0, -CGRectGetHeight(frame), CGRectGetWidth(frame), CGRectGetHeight(frame)); | |
break; | |
default: | |
case UIInterfaceOrientationPortrait: | |
return CGRectMake(0, CGRectGetHeight(frame), CGRectGetWidth(frame), CGRectGetHeight(frame)); | |
break; | |
} | |
} | |
} | |
- (void)animatePresentation:(id<UIViewControllerContextTransitioning>)transitionContext | |
{ | |
UIViewController* source = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; | |
UIViewController* destination = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; | |
UIView* container = transitionContext.containerView; | |
// Orientation bug fix | |
// See: http://stackoverflow.com/a/20061872/351305 | |
destination.view.frame = container.bounds; | |
source.view.frame = container.bounds; | |
// Add controllers | |
[container addSubview:destination.view]; | |
[container addSubview:source.view]; | |
// Move destination snapshot back in Z plane | |
// Important: original transform3d is different from CATransform3DIdentity | |
CATransform3D originalTransform = destination.view.layer.transform; | |
// Apply custom transform | |
CATransform3D perspectiveTransform = originalTransform; | |
perspectiveTransform.m34 = 1.0 / -1000.0; | |
perspectiveTransform = CATransform3DTranslate(perspectiveTransform, 0, 0, -100); | |
destination.view.layer.transform = perspectiveTransform; | |
// Start appearance transition for source controller | |
// Because UIKit does not do this automatically | |
[source beginAppearanceTransition:NO animated:YES]; | |
// Animate | |
[UIView animateKeyframesWithDuration:0.5 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{ | |
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:1.0 animations:^{ | |
source.view.frame = [self presentingControllerFrameWithContext:transitionContext]; | |
}]; | |
[UIView addKeyframeWithRelativeStartTime:0.2 relativeDuration:0.8 animations:^{ | |
destination.view.layer.transform = originalTransform; | |
}]; | |
} completion:^(BOOL finished) { | |
// Important: remove source view | |
// Otherwise it may show up on rotation.. | |
[source.view removeFromSuperview]; | |
// End appearance transition for source controller | |
[source endAppearanceTransition]; | |
// Finish transition | |
[transitionContext completeTransition:YES]; | |
}]; | |
} | |
- (void)animateDismissal:(id<UIViewControllerContextTransitioning>)transitionContext | |
{ | |
UIViewController* source = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; | |
UIViewController* destination = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; | |
UIView* container = transitionContext.containerView; | |
// Orientation bug fix | |
// See: http://stackoverflow.com/a/20061872/351305 | |
destination.view.frame = container.bounds; | |
source.view.frame = container.bounds; | |
// Add controllers | |
[container addSubview:source.view]; | |
[container addSubview:destination.view]; | |
// Start appearance transition for destination controller | |
// Because UIKit does not do this automatically | |
[destination beginAppearanceTransition:YES animated:YES]; | |
// Move destination view below source view | |
destination.view.frame = [self presentingControllerFrameWithContext:transitionContext]; | |
// Animate | |
[UIView animateKeyframesWithDuration:0.5 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{ | |
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:1.0 animations:^{ | |
destination.view.frame = container.bounds; | |
}]; | |
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:1.0 animations:^{ | |
// Important: original transform3d is different from CATransform3DIdentity | |
CATransform3D perspectiveTransform = source.view.layer.transform; | |
perspectiveTransform.m34 = 1.0 / -1000.0; | |
perspectiveTransform = CATransform3DTranslate(perspectiveTransform, 0, 0, -100); | |
source.view.layer.transform = perspectiveTransform; | |
}]; | |
} completion:^(BOOL finished) { | |
// End appearance transition for destination controller | |
[destination endAppearanceTransition]; | |
// Remove source view | |
[source.view removeFromSuperview]; | |
// | |
// iOS 8 beta 5 Patch: | |
// UIKit removes destination.view from hierarchy and leaves the blank screen. | |
// Manually adding UILayoutContainer to window hierarchy magically solves the problem. | |
// | |
if(floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) { | |
[destination.view.window addSubview:destination.view]; | |
} | |
// Finish transition | |
[transitionContext completeTransition:YES]; | |
}]; | |
} | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//How to trigger the segues | |
#pragma mark - Segues | |
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { | |
UIViewController* controller = (UIViewController*)segue.destinationViewController; | |
controller.transitioningDelegate = self; | |
controller.modalPresentationStyle = UIModalPresentationCustom; | |
controller.modalPresentationCapturesStatusBarAppearance = YES; | |
} | |
- (IBAction)unwindToRootViewController:(UIStoryboardSegue*)unwindSegue { | |
} | |
#pragma mark - Custom transitions | |
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { | |
return [ModalTransitionAnimator new]; | |
} | |
- (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { | |
return [ModalTransitionAnimator new]; | |
} | |
------------------------------------------------------------------------------------- | |
//The status bar appearance should be forwarded when use a navigation controller | |
#import "ModalNavigationController.h" | |
@implementation ModalNavigationController | |
// Forward status bar appearance to child view controller | |
- (UIViewController*)childViewControllerForStatusBarStyle { | |
return self.topViewController; | |
} | |
- (UIViewController*)childViewControllerForStatusBarHidden { | |
return self.topViewController; | |
} | |
@implementation ModalViewController | |
- (UIStatusBarStyle)preferredStatusBarStyle { | |
return UIStatusBarStyleLightContent; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment