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 转场动画探究(一)我们说到了转场要素的第四点,把那个 ...
随机推荐
- linux shell管道和xargs的区别
如上图,加了xargs的话相当于将上一个操作的结果作为命令执行前的操作,不加的话直接先把后面的命令运行一遍再操作
- windows 2008、2012防火墙添加入站规则教程(端口例外)
windows2008.2012的设置方法基本一样,以下是以windows2008为例做添加80端口的步骤. 1.依次点“控制面板”→“系统和安全”→“windows防火墙”→“高级设置”,打开“高级 ...
- 【bzoj1042】[HAOI2008]硬币购物-递推与动规-容斥原理
硬币购物 硬币购物一共有4种硬币.面值分别为c1,c2,c3,c4.某人去商店买东西,去了tot次.每次带di枚ci硬币,买si的价值的东西.请问每次有多少种付款方法. Input 第一行 c1,c2 ...
- windows 配置 apache的多个站点
windows 配置apache的多个站点 第一步打开apache的conf/extra/httpd-vhosts.conf,复制<VirtualHost></VirtualHost ...
- POJ 1144 割点
题意 :求割点的数量 #include<iostream> #include<stdio.h> #include<vector> #include<strin ...
- Codeforces 645D Robot Rapping Results Report【拓扑排序+二分】
题目链接: http://codeforces.com/problemset/problem/645/D 题意: 给定n个机器人的m个能力大小关系,问你至少要前几个大小关系就可以得到所有机器人的能力顺 ...
- Generate Parentheses(组合,回溯)
Given n pairs of parentheses, write a function to generate all combinations of well-formed parenthes ...
- codeforces 875F(基环外向树)
题意 有一个左边m个点,右边n个点的二分图(n,m<=1e5),左边每个点向右边恰好连两条权值相同的边. 求这个二分图的最优匹配 分析 对于这种二选一问题,即左边的a连向右边的b和c,权值为d, ...
- appleid
https://appleid.apple.com/#!&page=signin
- 鼠标放上去Div旋转特效代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...