小结一下跳转页面的动画效果实现思路!
代码移步GitHub
总结的转场动画是下面几个情况:
- 导航控制器的 Push 动画和 Pop 动画
- 普通控制器的 Present 动画和 Dismiss动画,
思路简析:
- 跳转的控制器遵守
UINavigationControllerDelegate
协议,从而实现 Pop 和 Push 的跳转动画。
通过 operation == UINavigationControllerOperationPush 或者 UINavigationControllerOperationPop 区分是 Push 还是 Pop
- (nullable id
)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC``` - 跳转的控制器遵守
UIViewControllerTransitioningDelegate
协议, 从而实现 Present 和 Dissmiss跳转动画
Present:
- (nullable id
)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
Dissmiss:
- (nullable id
)animationControllerForDismissedController:(UIViewController *)dismissed;```
- 上述的几个方法都是 返回一个遵守UIViewControllerAnimatedTransitioning协议的对象,而主要的动画实现就是写在这个对象当中!我们只要封装好这个类型的对象,在上述方法中返回对应实例对象就能实现动画效果!
UIViewControllerAnimatedTransitioning协议 主要实现两个方法
-(NSTimeInterval)transitionDuration: transitionContext: 跳转的时间
-(void)animateTransition: transitionContext 所要执行的动画在这里实现 ```
其中第二个方法传入的参数是泛型的
UIViewControllerContextTransitioning
对象
可以通过下面它的的实例方法获取我们需要展示动画的相关属性
# 这个方法获得的是 控制整个跳转的页面 (API 描述:这个视图是动画发生的地方(画布))(将要跳转到的控制器的view添加到画布上执行动画)
- (nullable UIView *)containerView
# Key 取值 UITransitionContextFromViewControllerKey (源控制器)UITransitionContextToViewControllerKey(目标控制器)
- (nullable __kindof UIViewController *)viewControllerForKey:(NSString *)key
# Key 取值 UITransitionContextFromViewKey(源视图) UITransitionContextToViewKey(目标视图)
- (nullable __kindof UIView *)viewForKey:(NSString *)key
简单效果展示:
上代码:
第一步: 是封装一个实现动画的类(PP_Transition),遵循UIViewControllerAnimatedTransitioning
协议
PP_Transition.h中
- 导入 UIKit 框架
import ```
- 定义一个枚举类型,方便我们区分场景
枚举判断使用场景
typedef NS_OPTIONS(NSUInteger, AnimatedScene)
{
AnimatedScenePush = 0, // 值为 0
AnimatedScenePop = 1 << 0, // 值为 2 的 0次
AnimatedScenePresent = 1 << 1, // 值为 2 的 1次
AnimatedSceneDissmiss = 1 << 2 // 值为 2 的 2次
};
- 声明一个自定义初始化方法
>```code
- (instancetype)initWithStytle:(AnimatedScene)scene;```
-------
####PP_Transition.m中
>- 延展一个属性 用来记录初始化的类型
>```code
{
AnimatedScene _scenceStyle;
}```
- 实现自定义初始化方法
>```code
- (instancetype)initWithStytle:(AnimatedScene)scene
{
if (self = [super init])
{
_scenceStyle = scene;
}
return self;
}
- 实现协议方法1: 转场动画持续时间
- (NSTimeInterval)transitionDuration:(id
)transitionContext{
return 1.0;
}``` - 实现协议方法2: 具体去实现转场的动画 (这里我就是简单的实现几个动画, 我们可以根据具体的情况加以实现)
- 如果我们想加一些更好的效果,可以尝试在画布(containerView) View 上加上一些自定义的View 用来遮挡或者当做背景(比如 Pop回来时候目标View变大时候加一个背景),记住最后移除掉就行!这是一种思路!
- (void)animateTransition:(id)transitionContext
{
// 获取到 containerView视图 (我们动画发生的载体)
UIView *containerView = [transitionContext containerView];
// 我们要去的 View
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
// 从哪个 View 去的
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
switch (_scenceStyle)
{
case AnimatedScenePush:
{// Push 动画 这里只是举个例子 动画效果可以自己去使用
// 注意点: 一定要把目的视图(要去的 View) 添加到容器(containerView)上.
[UIView animateWithDuration:1.0 animations:^{
fromView.transform = CGAffineTransformMakeScale(0.5, 0.5);
} completion:^(BOOL finished) {
[containerView addSubview:toView];
// 这个方法大概就是完成过渡动画,更新内部视图,控制器状态的转变!
[transitionContext completeTransition:YES];
}];
NSLog(@"Push 动画效果");
}
break;
case AnimatedScenePop:
{
// Pop 动画
[UIView animateWithDuration:1.0 animations:^{
// 让当前的二级页面 从下方消失
fromView.frame = CGRectMake(0, kScreenH, kScreenW, kScreenH);
} completion:^(BOOL finished) {
[containerView addSubview:toView];
[UIView animateWithDuration:1.0 animations:^{
// 让首级页面 由小变大
toView.transform = CGAffineTransformMakeScale(1, 1);
} completion:^(BOOL finished) {
// 完成过度动画
[transitionContext completeTransition:YES];
}];
}];
NSLog(@"Pop 动画效果");
}
break;
case AnimatedScenePresent:
{
toView.frame = CGRectMake(kScreenW / 2.0 , kScreenH / 2.0, 0, 0);
[containerView addSubview:toView];
[UIView animateWithDuration:1.0 animations:^{
toView.frame = CGRectMake(0 , 0, kScreenW, kScreenH);
} completion:^(BOOL finished) {
[transitionContext completeTransition:YES];
}];
NSLog(@"Present 动画效果");
}
break;
case AnimatedSceneDissmiss:
{
[UIView animateWithDuration:1.0 animations:^{
// 让当前的二级页面 从上方消失
fromView.frame = CGRectMake(0, -kScreenH, kScreenW, kScreenH);
} completion:^(BOOL finished) {
[containerView addSubview:toView];
// 完成过度动画
[transitionContext completeTransition:YES];
}];
NSLog(@"Dissmiss 动画效果");
}
break;
default:
break;
}
}
在跳转的控制器中:
- 首先遵守
代理
- 设置代理
// 设置 导航控制器代理完成 push 和 pop
self.navigationController.delegate = self;
// 设置 模态转场过渡代理
目的控制器Vc.transitioningDelegate = self;
目的控制器Vc.modalPresentationStyle = UIModalPresentationCustom;```
>- 代理方法实现
```code
// 导航控制器的跳转动画代理方法 在这里完成 Push 和 Pop 动画
- (id)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{
return operation == UINavigationControllerOperationPush ? [[PP_Transition alloc] initWithStytle:(AnimatedScenePush)] : [[PP_Transition alloc] initWithStytle:(AnimatedScenePop)];
}
// 完成转场 Present 动画代理
- (id)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
NSLog(@"----------->Present 一级视图控制器中");
return [[PP_Transition alloc] initWithStytle:(AnimatedScenePresent)];
}
// 转场 Dissmiss 动画 代理
- (id)animationControllerForDismissedController:(UIViewController *)dismissed
{
NSLog(@"----------->Dissmiss 一级视图控制器中");
return [[PP_Transition alloc] initWithStytle:(AnimatedSceneDissmiss)];
}
最后说一句:
我开始写的类名不太好, 弄了一半想改的话可以这样!
.h选中要改的类名
---> 菜单栏选中 Edit
---> Refactor
---> Rename