前言
iOS 7
以后提供了自定義轉場動畫的功能,我們可以通過遵守協議完成自定義轉場動畫。本篇文章講解如何實現自定義present
、dismiss
自定義動畫。
效果圖
本篇文章實現的動畫切換效果圖如下:
視圖切換種類
如下效果圖,這是有兩大類視圖切換動畫的,一種是交互式的,另一種就是自定義的。
本篇只講其中的UIViewControllerAnimatedTransitioning
協議,來實現present
、dismiss
動畫效果。另外的幾個,后面會繼續學習總結!!!
協議
我們要實現present
、dismiss
自定義轉場效果,我們必須要有一個遵守了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
25
|
@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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
//
// HYBModalTransition.h
// PresentDismissTransitionDemo
//
// Created by huangyibiao on 15/12/21.
// Copyright © 2015年 huangyibiao. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSUInteger, HYBModalTransitionType) {
kHYBModalTransitionPresent = 1 << 1,
kHYBModalTransitionDismiss = 1 << 2
};
@interface HYBModalTransition : NSObject <UIViewControllerAnimatedTransitioning>
/*!
* @author 黃儀標, 15-12-21 11:12:44
*
* 指定動畫類型
*
* @param type 動畫類型
* @param duration 動畫時長
* @param presentHeight 彈出呈現的高度
* @param scale fromVC的綻放系數
*
* @return
*/
+ (HYBModalTransition *)transitionWithType:(HYBModalTransitionType)type
duration:(NSTimeInterval)duration
presentHeight:(CGFloat)presentHeight
scale:(CGPoint)scale;
@end
|
我們只公開了一個方法來創建,指定動畫類型,動畫時長,呈現的高度,縮放系數。
實現文件
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
|
//
// HYBModalTransition.m
// PresentDismissTransitionDemo
//
// Created by huangyibiao on 15/12/21.
// Copyright © 2015年 huangyibiao. All rights reserved.
//
#import "HYBModalTransition.h"
@interface HYBModalTransition ()
@property (nonatomic, assign) HYBModalTransitionType type;
@property (nonatomic, assign) CGFloat presentHeight;
@property (nonatomic, assign) CGPoint scale;
@property (nonatomic, assign) NSTimeInterval duration;
@end
@implementation HYBModalTransition
+ (HYBModalTransition *)transitionWithType:(HYBModalTransitionType)type
duration:(NSTimeInterval)duration
presentHeight:(CGFloat)presentHeight
scale:(CGPoint)scale {
HYBModalTransition *transition = [[HYBModalTransition alloc] init];
transition.type = type;
transition.presentHeight = presentHeight;
transition.scale = scale;
transition.duration = duration;
return transition;
}
#pragma mark - UIViewControllerAnimatedTransitioning
- (void)animationEnded:(BOOL)transitionCompleted {
NSLog(@"%s", __FUNCTION__);
}
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return self.duration;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
switch (self.type) {
case kHYBModalTransitionPresent: {
[self present:transitionContext];
break;
}
case kHYBModalTransitionDismiss: {
[self dismiss:transitionContext];
break;
}
default: {
break;
}
}
}
#pragma mark - Private
- (void)present:(id<UIViewControllerContextTransitioning>)transitonContext {
UIViewController *fromVC = [transitonContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitonContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitonContext containerView];
// 對fromVC.view的截圖添加動畫效果
UIView *tempView = [fromVC.view snapshotViewAfterScreenUpdates:NO];
tempView.frame = fromVC.view.frame;
// 對截圖添加動畫,則fromVC可以隱藏
fromVC.view.hidden = YES;
// 要實現轉場,必須加入到containerView中
[containerView addSubview:tempView];
[containerView addSubview:toVC.view];
// 我們要設置外部所傳參數
// 設置呈現的高度
toVC.view.frame = CGRectMake(0,
containerView.frame.size.height,
containerView.frame.size.width,
self.presentHeight);
// 開始動畫
__weak __typeof(self) weakSelf = self;
[UIView animateWithDuration:self.duration delay:0.0 usingSpringWithDamping:0.5 initialSpringVelocity:1.0 / 0.5 options:0 animations:^{
// 在Y方向移動指定的高度
toVC.view.transform = CGAffineTransformMakeTranslation(0, -weakSelf.presentHeight);
// 讓截圖縮放
tempView.transform = CGAffineTransformMakeScale(weakSelf.scale.x, weakSelf.scale.y);
} completion:^(BOOL finished) {
if (finished) {
[transitonContext completeTransition:YES];
}
}];
}
- (void)dismiss:(id<UIViewControllerContextTransitioning>)transitonContext {
UIViewController *fromVC = [transitonContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitonContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *containerView = [transitonContext containerView];
// 取出present時的截圖用於動畫
UIView *tempView = containerView.subviews.lastObject;
// 開始動畫
[UIView animateWithDuration:self.duration animations:^{
toVC.view.transform = CGAffineTransformIdentity;
fromVC.view.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
if (finished) {
[transitonContext completeTransition:YES];
toVC.view.hidden = NO;
// 將截圖去掉
[tempView removeFromSuperview];
}
}];
}
@end
|
我們這里就不細講了,因為在iOS 7 push/pop轉場動畫中已經講過了。大家若未看過,可以先閱讀。
測試效果
我們要設置一下被present
的控制器的代理,在-viewDidLoad:
時添加如下代碼:
1
2
3
4
5
|
// 配置一下代理防呈現樣式為自定義
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationCustom;
|
同時,還需要遵守協議並實現協議UIViewControllerTransitioningDelegate
,這個是控制器轉場動畫實現的代理:
1
2
3
4
5
6
7
8
9
10
|
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
return [HYBModalTransition transitionWithType:kHYBModalTransitionPresent duration:0.5 presentHeight:350 scale:CGPointMake(0.9, 0.9)];
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
return [HYBModalTransition transitionWithType:kHYBModalTransitionDismiss duration:0.25 presentHeight:350 scale:CGPointMake(0.9, 0.9)];
}
|
我們設置present
、dismiss
自定義對象,就可以實現我們的動畫了。
想要實現什么樣的動畫,都可以在HYBModalTransition
類里面實現,沒有實現不了,只有想不到!!!