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 转场动画探究(一)我们说到了转场要素的第四点,把那个 ...
随机推荐
- zoj 1240
IBM Minus One Time Limit: 2 Seconds Memory Limit: 65536 KB You may have heard of the book '2001 ...
- rsync 同步文件重复拷贝问题
rsync 是同步文件的利器,一般用于多个机器之间的文件同步与备份,同时也支持在本地的不同目录之间互相同步文件.在这种场景下,rsync 远比 cp 命令更加合适,它只会同步需要更新的文件,默认情况下 ...
- 间谍网络(tarjan缩点)
洛谷传送门 看着这道题给人感觉就是tarjan求SCC,然而还得判断是否能控制全部间谍,这就得先从可以贿赂的点dfs一遍. 如果没有全部被标记了,就输出NO,再从没被标记的点里找最小的标号. 如果全被 ...
- CF830B:Cards Sorting
对叠放着的n张牌,第i张牌写有数字Ai,进行操作:将牌堆顶的牌取出,若是当前牌堆最小值就扔掉,否则放到牌堆底,求牌堆空时操作次数. 怎么看怎么像约瑟夫..不过约瑟夫DP我不太熟,于是就yy了一下 “当 ...
- Python基础教程笔记——第5章:条件,循环和其他语句
5.1 print和import的更多信息 1. print()3.0之后print不再是语句,而是函数, >>> print('udg',12,13) udg 12 13 &g ...
- Linux kernel 内核学习路线
看了下各位大神的推荐路线,总结如下: 0. 跟着项目走: 1. 学会用.熟练用linux系统: 2. Linux Kernel Development. 3. Understanding the Li ...
- HDU 1024 Max Sum Plus Plus【DP,最大m子段和】
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1024 题意: 给定序列,给定m,求m个子段的最大和. 分析: 设dp[i][j]为以第j个元素结尾的 ...
- Codeforces 658B Bear and Displayed Friends【set】
题目链接: http://codeforces.com/contest/658/problem/B 题意: 给定元素编号及亲密度,每次插入一个元素,并按亲密度从大到小排序.给定若干操作,回答每次询问的 ...
- 寒武纪camp Day3
补题进度:9/10 A(多项式) 题意: 在一个长度为n=262144的环上,一个人站在0点上,每一秒钟有$\frac{1}{2}$的概率待在原地不动,有$\frac{1}{4}$的概率向前走一步,有 ...
- json三种类型小笔记
JSON基本语法与图例 Object(对象类型)用{ }包含一系列无序的key–Value键值对表示,其中Key和Value之间用冒号分割,每个key-value之间用逗号分割. Array(数组类型 ...