概述
本文内容是直接从github上找了一个demo,分析了下其源码实现,仅供入门参考,原始链接:https://github.com/YanLYM/YMTransitionDemo
开门动画效果
- 如图push的时候,当前fromVC从中间往两端逐渐消失,toVC逐渐展示出来;
-
关闭pop动画正好相反,不分析;
image.png
实现源码分析
01 AppDelegate代码
- 没有特殊逻辑,仅仅是用了UINavigationController包裹了一下;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
ViewController * viewControllView = [[ViewController alloc] init];
UINavigationController * na = [[UINavigationController alloc] initWithRootViewController:viewControllView];
self.window.rootViewController = na;
[self.window makeKeyAndVisible];
return YES;
}
02 AppDelegate代码
- YMOpenViewController是一个普通的UIVC子类,唯一的特殊点是实现了<UINavigationControllerDelegate>协议
YMOpenViewController *vc = [YMOpenViewController new];
self.navigationController.delegate = vc;
[self.navigationController pushViewController:vc animated:YES];
03 YMOpenViewController代码
- 这里我们只看是如何实现<UINavigationControllerDelegate>协议中的下面这个方法的,别的都没有实现
- 从代码可以看到,这个方法需要返回一个实现了<UIViewControllerAnimatedTransitioning>协议的对象,self.animation.isPop这个赋值仅仅是为了记录当前是push还是pop。
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation//None/Push/Pop
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
if (operation == UINavigationControllerOperationPush) {
self.animation.isPop = NO;
} else if (operation == UINavigationControllerOperationPop) {
self.animation.isPop = YES;
}
return self.animation;
}
- (YMOpenAnimation *)animation {
if (nil == _animation) {
_animation = [[YMOpenAnimation alloc] init];
}
return _animation;
}
04 YMOpenAnimation代码
- YMOpenAnimation肯定需要实现<UIViewControllerAnimatedTransitioning>协议,上面已经说了。这个协议有两个方法需要实现(其他的先不管),一个是定义动画时长,比较简单;另外一个是定义动画效果是啥,我们看看代码
@protocol UIViewControllerAnimatedTransitioning <NSObject>
// This is used for percent driven interactive transitions, as well as for
// container controllers that have companion animations that might need to
// synchronize with the main animation.
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
// This method can only be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
@optional
...
@end
// 我们只看这一个协议方法是如何实现的
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
if (!self.isPop) {
[self pushWithTransition:transitionContext];
} else {
[self popWithTransition:transitionContext];
}
}
// 我们只看push动画(如何开门)
- (void)pushWithTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
// transitionContext其实就类似一个context
UIView *containerView = [transitionContext containerView];
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
// 截两个快照用于做动画
UIView *leftFromView = [fromView snapshotViewAfterScreenUpdates:NO];
leftFromView.frame = fromView.frame;
UIView *rightFromView = [fromView snapshotViewAfterScreenUpdates:NO];
rightFromView.frame = CGRectMake(-fromView.frame.size.width/2, 0, fromView.frame.size.width, fromView.frame.size.height);
// 定义开门动画
UIView *leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, fromView.frame.size.width/2, fromView.frame.size.height)];
leftView.clipsToBounds = YES;
UIView *rightView = [[UIView alloc] initWithFrame:CGRectMake(fromView.frame.size.width/2, 0, fromView.frame.size.width/2, fromView.frame.size.height)];
rightView.clipsToBounds = YES;
[leftView addSubview:leftFromView];
[rightView addSubview:rightFromView];
[containerView addSubview:toView];
[containerView addSubview:leftView];
[containerView addSubview:rightView];
NSTimeInterval interval = [self transitionDuration:transitionContext];
// 最终就是一个UIView动画
[UIView animateWithDuration:interval animations:^{
leftView.frame = CGRectMake(-fromView.frame.size.width/2, 0, fromView.frame.size.width/2, fromView.frame.size.height);
rightView.frame = CGRectMake(fromView.frame.size.width, 0, fromView.frame.size.width/2, fromView.frame.size.height);
} completion:^(BOOL finished) {
// 最后把额外的view都remove掉再
BOOL cancel = [transitionContext transitionWasCancelled];
[transitionContext completeTransition:!cancel];
if (!cancel) {
[leftView removeFromSuperview];
[rightView removeFromSuperview];
}
}];
}
网友评论