自定義轉場動畫主用到3個類
- 實現UIViewControllerAnimatedTransitioning協議的動畫類
- 繼承UIPercentDrivenInteractiveTransition類的手勢類
- 實現UIViewControllerContextTransitioning協議的transitionContext
先實現present和dismiss動畫
PresentedViewController *vc = [[PresentedViewController alloc] init];
//設置動畫的代理,vc需要實現UIViewControllerTransitioningDelegate協議
vc.transitioningDelegate = vc;
[self presentViewController:vc animated:YES completion:NULL];
UIViewControllerTransitioningDelegate協議裏方法主要用到一下四個方法
//返回present的動畫實例
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//返回dismiss的動畫實例
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
//返回present動畫手勢實例
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator;
//返回dismiss動畫手勢實例
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator;
這裏主要介紹dismiss的動畫和手勢
我們建立了一個動畫類SunAimator,實現了UIViewControllerAnimatedTransitioning協議,下面是協議兩個方法的實現
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext {
//動畫時間
return 1.5;
}
- (void)dismissAnimatorTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
//將要消失的視圖
UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
//將要顯示的視圖
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
//動畫過程的背景視圖
UIView *containerView = [transitionContext containerView];
//動畫時間
NSTimeInterval duration = [self transitionDuration:transitionContext];
//應爲要在fromView上做動畫,爲了一些不必要的麻煩這裏採用截圖處理
UIView *tempView = [self snapWithView:fromView];
//將參與動畫的視圖添加到動畫背景視圖上
[containerView addSubview:toView];
[containerView addSubview:tempView];
//動畫的具體內容,也可自自己使用CABasicAnimation、CAKeyframeAnimation和CAKeyframeAnimation實現更復雜的動畫
[UIView animateWithDuration:duration animations:^{
CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1);
tempView.transform = transform;
} completion:^(BOOL finished) {
//判斷手勢是否失敗
if([transitionContext transitionWasCancelled]){
//發送一個失敗的消息
[transitionContext completeTransition:NO];
//失敗的話需要將目標視圖移除,還要將原先的頁面顯示出來(顯示很重要)
[toView removeFromSuperview];
[containerView addSubview:fromView];
}else{
[transitionContext completeTransition:YES];
}
//臨時視圖不管動畫失敗還是成功都要移除
[tempView removeFromSuperview];
}];
}
下面是手勢類SunDrivenInteractive,手勢需要繼承UIPercentDrivenInteractiveTransition類
SunDrivenInteractive.h
@interface SunDrivenInteractive : UIPercentDrivenInteractiveTransition
@property (nonatomic,assign) BOOL isIneractive;
- (void)addGestureToViewCOntroller:(UIViewController *)fromVC;
@end
SunDrivenInteractive.m
#import "SunDrivenInteractive.h"
@interface SunDrivenInteractive ()
@property (nonatomic,weak) UIViewController *fromVC;
@property (nonatomic,assign) CGFloat percent;
@end
@implementation SunDrivenInteractive
- (void)addGestureToViewCOntroller:(UIViewController *)fromVC {
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveAction:)];
[fromVC.view addGestureRecognizer:panGesture];
_fromVC = fromVC;
}
- (void)moveAction:(UIPanGestureRecognizer *)panGesture {
if(panGesture.state == UIGestureRecognizerStateBegan){
_isIneractive = YES;
[_fromVC dismissViewControllerAnimated:YES completion:NULL];
}else if (panGesture.state == UIGestureRecognizerStateChanged){
CGFloat translationX = [panGesture translationInView:_fromVC.view].x;
if(translationX > 0){
_percent = translationX/300.0;
//更新進度
[self updateInteractiveTransition:_percent];
}
}else if (panGesture.state == UIGestureRecognizerStateEnded){
_isIneractive = NO;
if(_percent > 0.5){
[self finishInteractiveTransition];
}else{
[self cancelInteractiveTransition];
}
}
}
動畫類和手勢類都已經準備好了,接下里是PresentedViewController.m裏的實現
@interface PresentedViewController ()
@property (nonatomic,strong) SunDrivenInteractive *interactive;
@end
@implementation PresentedViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view.backgroundColor = [UIColor yellowColor];
_interactive = [[SunDrivenInteractive alloc] init];
[_interactive addGestureToViewCOntroller:self];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self dismissViewControllerAnimated:YES completion:NULL];
}
#pragma mark - UIViewControllerTransitioningDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source {
//present動畫
return [[SunAimator alloc] initWithAnimatorType:(SunAimatorTypePresent)];
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed {
//dismiss動畫
return [[SunAimator alloc] initWithAnimatorType:(SunAimatorTypeDismiss)];
}
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator {
//present手勢
return nil;
}
- (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator {
//dismiss手勢
return (_interactive.isIneractive?_interactive:nil);
}
簡單的轉場動畫就這樣實現了
Demo地址