iOS7 push/pop转场动画
前言
iOS 7之后,苹果提供了自定义转场动画的API,我们可以自己去定义任意动画效果。本篇为笔者学习push、pop自定义转场效果的笔记,如何有任何不正确或者有指导意见的,请在评论中留下您的宝贵意见!!!
请注意:如果要求支持iOS 7以下版本,则不可使用此效果。
实现目标效果
我们本篇文章目标效果:

视图切换种类
如下效果图,这是有两大类视图切换动画的,一种是交互式的,另一种就是自定义的。

本篇只讲其中的UIViewControllerAnimatedTransitioning协议,来实现push、pop动画效果。另外的几个,后面会继续学习总结!!!
协议
我们要实现push、pop自定义转场效果,我们必须要有一个遵守了UIViewControllerAnimatedTransitioning协议且实现其必须实现的代理方法的类。
我们先来学习UIViewControllerAnimatedTransitioning协议:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@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.
//
// 指定转场动画时长,必须实现,否则会Crash。
// 这个方法是为百分比驱动的交互转场和有对比动画效果的容器类控制器而定制的。
- (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
// This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked.
- (void)animationEnded:(BOOL) transitionCompleted;
@end
|
我们要实现目标效果,就需要一个定义一个类遵守UIViewControllerAnimatedTransitioning协议并实现相应的代理方法。
遵守UIViewControllerAnimatedTransitioning协议
下面,我们来定义一个转场类,这个类必须要遵守UIViewControllerAnimatedTransitioning协议,如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//
// HYBControllerTransition.h
// PushPopMoveTransitionDemo
//
// Created by huangyibiao on 15/12/18.
// Copyright © 2015年 huangyibiao. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, HYBControllerTransitionType) {
kControllerTransitionPush = 1 << 1,
kControllerTransitionPop = 1 << 2
};
@interface HYBControllerTransition : NSObject <UIViewControllerAnimatedTransitioning>
+ (instancetype)transitionWithType:(HYBControllerTransitionType)transitionType
duration:(NSTimeInterval)duration;
@end
|
我们只需要公开一个工厂方法来生成对象即可,调用更简单些。第个参数用于指定是哪种类型,是push还是pop,第二个参数是用于指定动画时长。
实现文件
我们一步步分析下面的关键代码:
|
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
//
// HYBControllerTransition.m
// PushPopMoveTransitionDemo
//
// Created by huangyibiao on 15/12/18.
// Copyright © 2015年 huangyibiao. All rights reserved.
//
#import "HYBControllerTransition.h"
#import "ViewController.h"
#import "DetailController.h"
@interface HYBControllerTransition ()
@property (nonatomic, assign) HYBControllerTransitionType transitionType;
@property (nonatomic, assign) NSTimeInterval duration;
@end
@implementation HYBControllerTransition
- (instancetype)init {
if (self = [super init]) {
self.transitionType = kControllerTransitionPush;
}
return self;
}
+ (instancetype)transitionWithType:(HYBControllerTransitionType)transitionType
duration:(NSTimeInterval)duration {
HYBControllerTransition *transition = [[HYBControllerTransition alloc] init];
transition.transitionType = transitionType;
transition.duration = duration;
return transition;
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
switch (self.transitionType) {
case kControllerTransitionPush: {
[self push:transitionContext];
break;
}
case kControllerTransitionPop: {
[self pop:transitionContext];
break;
}
default: {
break;
}
}
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return self.duration;
}
- (void)animationEnded:(BOOL)transitionCompleted {
NSLog(@"%s", __FUNCTION__);
}
#pragma mark - Private
- (void)pop:(id<UIViewControllerContextTransitioning>)transitionContext {
DetailController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
ViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
UIView *toImageView = toVC.isImg1 ? toVC.img1 : toVC.img2;
UIView *tempView = containerView.subviews.lastObject;
// 第一个view是fromVC.view
// 第二个view是push进来时所生成的toImageView截图
for (UIView *view in containerView.subviews) {
NSLog(@"%@", view);
if (fromVC.view == view) {
NSLog(@"YES");
}
}
toImageView.hidden = YES;
tempView.hidden = NO;
// 必须保证将toVC.view放在最上面,也就是第一个位置
[containerView insertSubview:toVC.view atIndex:0];
[UIView animateWithDuration:self.duration
delay:0.0
usingSpringWithDamping:0.55
initialSpringVelocity:1/ 0.55
options:0
animations:^{
fromVC.view.alpha = 0.0;
tempView.frame = [toImageView convertRect:toImageView.bounds toView:containerView];
} completion:^(BOOL finished) {
tempView.hidden = NO;
toImageView.hidden = NO;
[tempView removeFromSuperview];
[transitionContext completeTransition:YES];
for (UIView *view in containerView.subviews) {
NSLog(@"%@", view);
if (toVC.view == view) {
NSLog(@"YES");
}
}
}];
}
- (void)push:(id<UIViewControllerContextTransitioning>)transitionContext {
ViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
DetailController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitionContext containerView];
UIView *fromImageView = fromVC.isImg1 ? fromVC.img1 : fromVC.img2;
UIView *tempView = [fromImageView snapshotViewAfterScreenUpdates:NO];
tempView.frame = [fromImageView convertRect:fromImageView.bounds toView:containerView];
UIView *toImageView = toVC.imgView;
fromImageView.hidden = YES;
toVC.view.alpha = 0.0;
toImageView.hidden = YES;
[containerView addSubview:toVC.view];
[containerView addSubview:tempView];
[UIView animateWithDuration:self.duration
delay:0.0
usingSpringWithDamping:0.55
initialSpringVelocity:1/ 0.55
options:0
animations:^{
toVC.view.alpha = 1.0;
tempView.frame = [toImageView convertRect:toImageView.bounds toView:containerView];
} completion:^(BOOL finished) {
tempView.hidden = YES;
toImageView.hidden = NO;
[transitionContext completeTransition:YES];
}];
}
@end
|
分析Push动画
我们暂不细说UIViewControllerContextTransitioning协议,我们这里只使用到了-containerView这个代理方法,我们可以通过苹果提供的键来获取对应的控制器:
|
1
2
3
4
|
UIKIT_EXTERN NSString *const UITransitionContextFromViewControllerKey NS_AVAILABLE_IOS(7_0);
UIKIT_EXTERN NSString *const UITransitionContextToViewControllerKey NS_AVAILABLE_IOS(7_0);
|
我们可以看到这是在iOS 7.0以后才有的,因此系统版本要求是在iOS 7才能使用。
我们这里通过:
|
1
2
3
|
ViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
|
获取到了fromVC,也就是当前要从哪个控制器切换。
然后通过:
|
1
2
3
|
DetailController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
|
获取到了toVC,也就是切换到哪一个控制器。
然后再通过
|
1
2
3
|
UIView *containerView = [transitionContext containerView];
|
获取containerView视图。这个是一个代理方法,可以获取到视图容器。
下面我们获取fromVC所点击的图片控件,然后通过-snapshotViewAfterScreenUpdates:将所点击的图片控件截图并用于切换使用,参数设置为NO,否则动画会很生硬。最后,我们还要将这个所点击的图片控件的坐标转换成容器视图的坐标:
|
1
2
3
4
5
|
UIView *fromImageView = fromVC.isImg1 ? fromVC.img1 : fromVC.img2;
UIView *tempView = [fromImageView snapshotViewAfterScreenUpdates:NO];
tempView.frame = [fromImageView convertRect:fromImageView.bounds toView:containerView];
|
将下来就是设置切换动画之前的状态:
|
1
2
3
4
5
6
7
|
UIView *toImageView = toVC.imgView;
fromImageView.hidden = YES;
toVC.view.alpha = 0.0;
toImageView.hidden = YES;
|
下面这两行是非常关键的,并且必须保证tempView在最上层,否则动画效果就没有了。先将目标控制器的视图添加到容器中,再添加源图片的截图到容器中,用于显示切换效果。
|
1
2
3
4
|
[containerView addSubview:toVC.view];
[containerView addSubview:tempView];
|
我们在动画中,将初始的截图的frame改变成最终的效果的frame即可达到我们的目标效果。另外要注意还需要将坐标转换成容器的坐标:
|
1
2
3
|
tempView.frame = [toImageView convertRect:toImageView.bounds toView:containerView];
|
当动画完成以后,一定要调用:[transitionContext completeTransition:YES],设置切换动画已经完成,否则想要pop回去就不能了。
分析pop动画
我们只讲不同于push部分的代码,我们添加了打印容器中的视图的代码:
|
1
2
3
4
5
6
7
8
9
10
|
// 第一个view是fromVC.view
// 第二个view是push进来时所生成的toImageView截图
for (UIView *view in containerView.subviews) {
NSLog(@"%@", view);
if (fromVC.view == view) {
NSLog(@"YES");
}
}
|
打印结果:
|
1
2
3
4
5
6
7
8
|
<UIView: 0x7fae00514ef0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fae00513320>>
YES
<_UIReplicantView: 0x7fae00566bd0; frame = (20 20; 335 627); hidden = YES; layer = <_UIReplicantLayer: 0x7fae00510520>>
<UIView: 0x7fae004563a0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7fae00422b20>>
YES
|
从打印结果分析出来,在pop之前,第一个就是fromVC.view,第二个就是上次push时的截图。在动画完成之后,只剩下toVC.view了。
我们还需要将toVC.view放在容器最上层:
|
1
2
3
4
|
// 必须保证将toVC.view放在最上面,也就是第一个位置
[containerView insertSubview:toVC.view atIndex:0];
|
动画完成后,一定要将tempView移除:
|
1
2
3
|
[tempView removeFromSuperview];
|
如果不移除,这个tempView会与所点击的图片控件等大小,位置一样,会挡住原图片控件,手势也就无法响应。我们一定要注意移除。动画完成后,也一定要设置[transitionContext completeTransition:YES]。
iOS7 push/pop转场动画的更多相关文章
- iOS_SN_push/pop转场动画封装和一般动画封装
封装类中的方法: #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface AnimationE ...
- iOS 7 present/dismiss转场动画
前言 iOS 7以后提供了自定义转场动画的功能,我们可以通过遵守协议完成自定义转场动画.本篇文章讲解如何实现自定义present.dismiss自定义动画. 效果图 本篇文章实现的动画切换效果图如下: ...
- 自定义Push/Pop和Present/Dismiss转场
项目概述 iOS中最常见的动画无疑是Push和Pop的转场动画了,其次是Present和Dismiss的转场动画. 如果我们想自定义这些转场动画,苹果其实提供了相关的API,在自定义转场之前,我们需要 ...
- 类似nike+、香蕉打卡的转场动画效果-b
首先,支持并感谢@wazrx 的 http://www.jianshu.com/p/45434f73019e和@onevcat 的https://onevcat.com/2013/10/vc-tran ...
- 第六十五篇、OC_iOS7 自定义转场动画push pop
自定义转场动画,在iOS7及以上的版本才开始出现的,在一些应用中,我们常常需要定制自定义的的跳转动画 1.遵守协议:<UIViewControllerAnimatedTransitioning& ...
- Swift基础之自定义PUSH和POP跳转动画
之前用OC代码写过PUSH和POP的转场动画,闲来无事,将其转换成Swift语言,希望对大家有帮助,转载请注明.... 如何实现PUSH和POP的转场动画? 首先,创建一个NSObject的类,分别用 ...
- iOS 开发--转场动画
"用过格瓦拉电影,或者其他app可能都知道,一种点击按钮用放大效果实现转场的动画现在很流行,效果大致如下:" 本文主讲SWIFT版,OC版在后面会留下Demo下载 在iOS中,在同 ...
- iOS自定义转场动画的实现
iOS中熟悉的是导航栏中的push和pop这两种动画效果,在这里我们可以自己实现自己想要的一些转场动画 下面是我自己创建转场动画的过程 1.新建一个文件继承自NSObject ,遵循协议UIViewC ...
- iOS 转场动画探究(二)
这篇文章是接着第一篇写的,要是有同行刚看到的话建议从前面第一篇看,这是第一篇的地址:iOS 转场动画探究(一) 接着上一篇写的内容: 上一篇iOS 转场动画探究(一)我们说到了转场要素的第四点,把那个 ...
随机推荐
- 【Java 理论篇 1】Java2平台的三个版本介绍
导读:关于java的三种分类J2SE.J2EE.J2ME,在网上有很多资料,然后自己写的,也大多是从各个网站上搜罗里的.算是自己的一种笔记,或者明白的说,就是把别人的东西抄一遍.但是,这对于我来说,也 ...
- CSU 1605 数独
题目大意: 9宫格每个位置都有对应的分数,填完数独后根据对应位置的分数相加之和求个最大值,不存在输出-1 说什么用位运算加速可以解决问题,但是对着标程还是T,最近学了dlx,发现这样解决数独快了很多 ...
- [luoguP2962] [USACO09NOV]灯Lights(高斯消元 + dfs)
传送门 先进行高斯消元 因为要求最少的开关次数,那么: 对于关键元,我们可以通过带入消元求出, 对于自由元,我们暴力枚举,进行dfs,因为只有开关两种状态,0或1 #include <cmath ...
- [转]SQL SERVER数据库还原的方法
SQL SERVER数据库还原的方法 在SQL SERVER 2005下还原数据库 1.新建数据库A,右键还原数据库,此时目标数据库为A,选择备份 文件B_db_201311040200.BAK,还原 ...
- Circling Round Treasures(codeforces 375c)
题意:要求在一张网格图上走出一条闭合路径,不得将炸弹包围进去,使围出的总价值减去路径长度最大. /* 类似于poj3182的做法,只不过出现了多个点,那么就用状态压缩的方法记录一个集合即可. */ # ...
- Easy sssp(vijos 1053)
描述 输入数据给出一个有N(2 <= N <= 1,000)个节点,M(M <= 100,000)条边的带权有向图. 要求你写一个程序, 判断这个有向图中是否存在负权回路. 如果从一 ...
- msp430入门编程36
msp430中C语言的可移植--面向接口实现
- poj2723 2sat判断解+二分
典型的2-sat问题,题意:有m个门,每个门上俩把锁,开启其中一把即可,现在给n对钥匙(所有 钥匙编号0123456...2n-1),每对钥匙只能用一把,要求尽可能开门多(按顺序,前max个). 关键 ...
- React学习及实例开发(一)——开始
本文基于React v16.4.1 初学react,有理解不对的地方,欢迎批评指正^_^ 一.构建一个新项目 1.命令行运行如下命令,构建一个新的react项目 npm install -g crea ...
- eclipse提速02 - eclipse.ini优化
给eclipse执行jvm.它可以让你使用自己的jdk,而不是系统环境变量所指定的jdk -vm /path/to/your/java 使用最新的jdk来运行eclipse.使用最新的jdk要好很多. ...