ios7以上苹果添加了一些新的push动画实现方式,下面是我对一些动画的封装(部分代码参照iOS_7_by_Tutorials_1_1这本书):
废话不多说,还是代码为上。
#import <Foundation/Foundation.h>
typedef enum : NSUInteger {
AnimatedTransitioningNone,
AnimatedTransitioningPop,
AnimatedTransitioningFlip,
AnimatedTransitioningShrink,
AnimatedTransitioningBounce,
AnimatedTransitioningContingent
} AnimatedTransitioningType;
typedef enum : NSUInteger {
InteractiveTransitioningNone,
InteractiveTransitioningSwipe
} InteractiveTransitioningType;
@class UserInteractionController;
@interface DHFModel_ControllerAnimation : NSObject
+ (id<UIViewControllerAnimatedTransitioning>)createAnimatedTransitioningWithType:(AnimatedTransitioningType)type;
+ (UserInteractionController *)createInteractiveTransitioningWithType:(InteractiveTransitioningType)type;
@end
#pragma mark - 用户交互操作
/**
* @author weloy, 15-11-28 09:11:21
*
* @brief 给toViewController添加pan手势
*
* @param viewController <#viewController description#>
*/
@interface UserInteractionController : UIPercentDrivenInteractiveTransition
- (void)wireToViewController:(UIViewController*)viewController;
@property (nonatomic, assign) BOOL interactionInProgress;
@end
#pragma mark - 动画
/**
* @author weloy, 15-11-28 09:11:32
*
* @brief pop回去的动画
*/
@interface PopAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@end
/**
* @author weloy, 15-11-28 09:11:44
*
* @brief 翻转动画
*/
@interface FlipAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic, assign) BOOL reverse;
@end
/**
* @author weloy, 15-11-28 09:11:54
*
* @brief 截图动画 不可用于pan手势返回
*/
@interface ShrinkDismissAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@end
/**
* @author weloy, 15-11-28 09:11:22
*
* @brief 弹跳动画
*/
@interface BouncePresentAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@end
@interface ContingentViewAnimationController : NSObject <UIViewControllerAnimatedTransitioning>
@property (nonatomic, strong) NSArray *fromViews;
@property (nonatomic, strong) NSArray *toViews;
@end
下面是.m文件
#import "DHFModel_ControllerAnimation.h"
#import <QuartzCore/QuartzCore.h>
@implementation DHFModel_ControllerAnimation
+ (id<UIViewControllerAnimatedTransitioning>)createAnimatedTransitioningWithType:(AnimatedTransitioningType)type{
id<UIViewControllerAnimatedTransitioning> animatedTransitioning = nil;
switch (type) {
case AnimatedTransitioningPop:
animatedTransitioning = [[PopAnimationController alloc]init];
break;
case AnimatedTransitioningBounce:
animatedTransitioning = [[BouncePresentAnimationController alloc] init];
break;
case AnimatedTransitioningFlip:
animatedTransitioning = [[FlipAnimationController alloc] init];
break;
case AnimatedTransitioningShrink:
animatedTransitioning = [[ShrinkDismissAnimationController alloc] init];
break;
case AnimatedTransitioningContingent:
animatedTransitioning = [[ContingentViewAnimationController alloc] init];
break;
default:
break;
}
return animatedTransitioning;
}
+ (id<UIViewControllerInteractiveTransitioning>)createInteractiveTransitioningWithType:(InteractiveTransitioningType)type{
UserInteractionController * interactiveTransitioning = nil;
switch (type) {
case InteractiveTransitioningSwipe:
interactiveTransitioning = [[UserInteractionController alloc] init];
break;
default:
break;
}
return interactiveTransitioning;
}
@end
@implementation UserInteractionController {
BOOL _shouldCompleteTransition;//是否要完成
__weak UIViewController *_viewController;
CGFloat _totalTranslationX;
BOOL _firstTimeChange;
}
- (void)wireToViewController:(UIViewController *)viewController {
_viewController = viewController;
[self prepareGestureRecognizerInView:viewController.view];
}
- (void)prepareGestureRecognizerInView:(UIView*)view {
UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
[view addGestureRecognizer:gesture];
_totalTranslationX = 0;
_firstTimeChange = YES;
}
- (CGFloat)completionSpeed
{
return 1;
}
- (void)handleGesture:(UIPanGestureRecognizer*)gestureRecognizer {
UIView * view = gestureRecognizer.view.superview;
CGPoint translation = [gestureRecognizer translationInView:view];
_totalTranslationX += translation.x;
[gestureRecognizer setTranslation:CGPointMake(0, 0) inView:view];
// NSLog(@"translation == %lf total == %f",translation.x,_totalTranslationX);
if (_totalTranslationX<0||translation.x<0.6) {
if (!self.interactionInProgress) {
[self cancelInteractiveTransition];
return;
}
}
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
// 1. Start an interactive transition!
[self beginTransition];
break;
case UIGestureRecognizerStateChanged: {
if (!self.interactionInProgress&&_firstTimeChange) {//有可能没有begin
if (_totalTranslationX > 5) {
[self beginTransition];
}
}
if (_firstTimeChange) {
// NSLog(@"\n\n\n");
_firstTimeChange = NO;
}
// 2. compute the current position
CGFloat fraction = fabs(_totalTranslationX / CGRectGetWidth(view.bounds));
// 3. should we complete?
_shouldCompleteTransition = (fraction > 0.2);
// NSLog(@"change");
// 4. update the animation controller
[self updateInteractiveTransition:fraction];
break;
}
case UIGestureRecognizerStateEnded:
case UIGestureRecognizerStateCancelled:
// 5. finish or cancel
self.interactionInProgress = NO;
if (!_shouldCompleteTransition || gestureRecognizer.state == UIGestureRecognizerStateCancelled) {
[self cancelInteractiveTransition];
}
else {
[self finishInteractiveTransition];
}
break;
default:
break;
}
}
- (void)cancelInteractiveTransition{
[super cancelInteractiveTransition];
// NSLog(@"cancel");
_totalTranslationX = 0;
_firstTimeChange = YES;
}
- (void)finishInteractiveTransition{
[super finishInteractiveTransition];
// NSLog(@"end");
_totalTranslationX = 0;
_firstTimeChange = YES;
}
- (void)beginTransition{
self.interactionInProgress = YES;
// NSLog(@"begin");
[_viewController.navigationController popViewControllerAnimated:YES];
}
- (void)dealloc{
}
@end
@implementation FlipAnimationController
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
// 1. the usual stuff ...
UIView* containerView = [transitionContext containerView];
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = toVC.view;
UIView *fromView = fromVC.view;
[containerView addSubview:toVC.view];
// 2. Add a perspective transform
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -0.002;
[containerView.layer setSublayerTransform:transform];
// 3. Give both VCs the same start frame
CGRect initialFrame = [transitionContext initialFrameForViewController:fromVC];
fromView.frame = initialFrame;
toView.frame = initialFrame;
// 4. reverse?
float factor = self.reverse ? 1.0 : -1.0;
// 5. flip the to VC halfway round - hiding it
toView.layer.transform = [self yRotation:factor * -M_PI_2];
// 6. Animate
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateKeyframesWithDuration:duration
delay:0.0
options:0
animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.5
animations:^{
// 7. rotate the from view
fromView.layer.transform = [self yRotation:factor * M_PI_2];
}];
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.5
animations:^{
// 8. rotate the to view
toView.layer.transform = [self yRotation:0.0];
}];
} completion:^(BOOL finished) {
BOOL cancel = [transitionContext transitionWasCancelled];
if (cancel) {
toView.layer.transform = [self yRotation:0.0];
fromView.layer.transform = [self yRotation:0.0];
}
[transitionContext completeTransition:!cancel];
}];
}
- (CATransform3D) yRotation:(CGFloat) angle {
return CATransform3DMakeRotation(angle, 0.0, 1.0, 0.0);
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.5f;
}
@end
@implementation ShrinkDismissAnimationController
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 1.0;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
// 1. obtain state from the context
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect finalFrame = [transitionContext finalFrameForViewController:toViewController];
// 2. obtain the container view
UIView *containerView = [transitionContext containerView];
// 3. set initial state
toViewController.view.frame = finalFrame;
toViewController.view.alpha = 0.5;
// 4. add the view
[containerView addSubview:toViewController.view];
[containerView sendSubviewToBack:toViewController.view];
// 1. Determine the intermediate and final frame for the from view
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGRect shrunkenFrame = CGRectInset(fromViewController.view.frame, fromViewController.view.frame.size.width/4, fromViewController.view.frame.size.height/4);
CGRect fromFinalFrame = CGRectOffset(shrunkenFrame, 0, screenBounds.size.height);
// create a snapshot
UIView *intermediateView = [fromViewController.view snapshotViewAfterScreenUpdates:NO];
intermediateView.frame = fromViewController.view.frame;
[containerView addSubview:intermediateView];
// remove the real view
[fromViewController.view removeFromSuperview];
NSTimeInterval duration = [self transitionDuration:transitionContext];
// animate with keyframes
[UIView animateKeyframesWithDuration:duration
delay:0.0
options:UIViewKeyframeAnimationOptionCalculationModeCubic
animations:^{
// 2a. keyframe one
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.5
animations:^{
intermediateView.frame = shrunkenFrame;
toViewController.view.alpha = 0.5;
}];
// 2b. keyframe two
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.5
animations:^{
intermediateView.frame = fromFinalFrame;
toViewController.view.alpha = 1.0;
}];
}
completion:^(BOOL finished) {
// 3. inform the context of completion
[intermediateView removeFromSuperview];
[transitionContext completeTransition:YES];
}];
}
@end
@implementation BouncePresentAnimationController
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
// 1. obtain state from the context
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
CGRect finalFrame = [transitionContext finalFrameForViewController:toViewController];
// 2. obtain the container view
UIView *containerView = [transitionContext containerView];
// 3. set initial state
CGRect screenBounds = [[UIScreen mainScreen] bounds];
toViewController.view.frame = CGRectOffset(finalFrame, 0, screenBounds.size.height);
// 4. add the view
[containerView addSubview:toViewController.view];
// 5. animate
NSTimeInterval duration = [self transitionDuration:transitionContext];
[UIView animateWithDuration:duration
delay:0.0
usingSpringWithDamping:0.6
initialSpringVelocity:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
fromViewController.view.alpha = 0.5;
toViewController.view.frame = finalFrame;
} completion:^(BOOL finished) {
// 6. inform the context of completion
BOOL cancel = [transitionContext transitionWasCancelled];
fromViewController.view.alpha = 1.0;
[transitionContext completeTransition:!cancel];
}];
}
@end
@implementation PopAnimationController
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.4;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIView *containerView = [transitionContext containerView];
[containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
CGRect finalFrame = [transitionContext finalFrameForViewController:toViewController];
UIView * bgView = [[UIView alloc] initWithFrame:toViewController.view.frame];
bgView.backgroundColor = [UIColor blackColor];
bgView.alpha = 0.5;
[containerView insertSubview:bgView belowSubview:fromViewController.view];
__block CGRect toRect = toViewController.view.frame;
__block CGRect fromRect = toRect;
CGFloat originX = toRect.origin.x;
toRect.origin.x -= toRect.size.width / 3;
toViewController.view.frame = toRect;
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
fromRect.origin.x += fromRect.size.width;
fromViewController.view.frame = fromRect;
bgView.alpha = 0;
toRect.origin.x = originX;
toViewController.view.frame = toRect;
} completion:^(BOOL finished) {
[bgView removeFromSuperview];
BOOL cancel = [transitionContext transitionWasCancelled];
if (cancel) {
toViewController.view.frame = finalFrame;
}
[transitionContext completeTransition:!cancel];
}];
}
@end
@implementation ContingentViewAnimationController
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
return 0.4;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
NSAssert([self.fromViews count] == [self.toViews count], @"*** The count of fromviews and toviews must be the same! ***");
NSMutableArray *fromViewSnapshotArray = [[NSMutableArray alloc] init];
for (UIView *fromView in self.fromViews) {
UIImageView *fromViewSnapshot = [[UIImageView alloc] initWithImage:[UIView imageWithView:fromView]];
fromViewSnapshot.frame = [containerView convertRect:fromView.frame fromView:fromViewController.view];
[fromViewSnapshotArray addObject:fromViewSnapshot];
fromView.hidden = YES;
}
for (UIView *toView in self.toViews) {
toView.hidden = YES;
}
toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
toViewController.view.alpha = 0;
[containerView addSubview:toViewController.view];
for (NSUInteger i = [fromViewSnapshotArray count]; i > 0; i--) {
[containerView addSubview:[fromViewSnapshotArray objectAtIndex:i - 1]];
}
[containerView layoutIfNeeded];
[UIView animateWithDuration:[self transitionDuration:transitionContext]
delay:0.0
usingSpringWithDamping:0.8
initialSpringVelocity:1.0
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
toViewController.view.alpha = 1.0;
for (NSUInteger i = 0; i < [self.fromViews count]; i++) {
UIView *toView = [self.toViews objectAtIndex:i];
UIView *fromViewSnapshot = [fromViewSnapshotArray objectAtIndex:i];
CGRect frame = [containerView convertRect:toView.frame fromView:toViewController.view];
fromViewSnapshot.frame = frame;
}
} completion:^(BOOL finished) {
for (NSUInteger i = 0; i < [self.fromViews count]; i++) {
UIView *toView = [self.toViews objectAtIndex:i];
UIView *fromView = [self.fromViews objectAtIndex:i];
toView.hidden = NO;
fromView.hidden = NO;
UIView *fromViewSnapshot = [fromViewSnapshotArray objectAtIndex:i];
[fromViewSnapshot removeFromSuperview];
}
[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
}];
}
@end