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 转场动画探究(一)我们说到了转场要素的第四点,把那个 ...
随机推荐
- struts2接受请求参数
https://blog.csdn.net/y249839817/article/details/77702745 https://blog.csdn.net/nthack5730/article/d ...
- Run-time Settings--General--Run Logic
LR单用户,重复操作日志 案例:假如你想在一个脚本中,实现登录执行1次,查询执行2次,插入执行3次,怎么办?录3个脚本?每个事务分别在脚本中复制N次? 当然不用,LR早就想到了你的需求,下面让我们隆重 ...
- php 时间 数组替换
//随机数和时间//随机数生产器//echo rand();//echo "<br>";//生成某个范围的随机数//echo rand(0,10);//echo &qu ...
- 动手实操:如何用 Python 实现人脸识别,证明这个杨幂是那个杨幂?
当前,人脸识别应用于许多领域,如支付宝的用户认证,许多的能识别人心情的 AI,也就是人的面部表情,还有能分析人的年龄等等,而这里面有着许多的难度,在这里我想要分享的是一个利用七牛 SDK 简单的实现人 ...
- Codeforces603E - Pastoral Oddities
Portal Description 初始时有\(n(n\leq10^5)\)个孤立的点,依次向图中加入\(m(m\leq3\times10^5)\)条带权无向边.使得图中每个点的度数均为奇数的边集是 ...
- Codeforces Round #533 (Div. 2) E 最大独立集
知识点 最大独立集(set) = 补图的最大团(clique ) 最小顶点覆盖 + 最大独立集 = V E. Helping Hiasat time limit per test 2 seconds ...
- [Poj2411]Mondriaan's Dream(状压dp)(插头dp)
Mondriaan's Dream Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 18096 Accepted: 103 ...
- java数据结构和算法09(哈希表)
树的结构说得差不多了,现在我们来说说一种数据结构叫做哈希表(hash table),哈希表有是干什么用的呢?我们知道树的操作的时间复杂度通常为O(logN),那有没有更快的数据结构?当然有,那就是哈希 ...
- Spring Cloud(8):Sleuth和Zipkin的使用
场景: 某大型电商网站基于微服务架构,服务模块有几十个. 某天,测试人员报告该网站响应速度过慢.排除了网络问题之后,发现很难进一步去排除故障. 那么:如何对微服务的链路进行监控呢? Sleuth: 一 ...
- vmware下centos6.7网络配置
使用NAT方式: 查看/etc/sysconfig/network-script/ 下面没有ifcfg-eth0 新建ifcfg-eth0,内容如下 DEVICE=eth0 BOOTPROTO=dhc ...