為模態(tài)視圖控制器添加交互式視圖轉(zhuǎn)換
參考
- GitHub 源碼:shinobicontrols/iOS7-day-by-day
- 天天品嘗iOS7甜點 :: Day 14 :: Interactive View Controller Transitions
這篇文章可能會用到之前兩篇文章的知識:
- 天天品嘗iOS7甜點 :: Day 10 :: Custom UIViewController Transitions - 導(dǎo)航視圖控制器自定義轉(zhuǎn)場
- 天天品嘗iOS7甜點 :: Day 11 :: UIView Key-frame Animations - 關(guān)鍵幀動畫
Flip Transition Animation - 翻轉(zhuǎn)過渡效果
默認(rèn)的模態(tài)視圖翻轉(zhuǎn)效果
自定義模態(tài)視圖翻轉(zhuǎn)效果
實現(xiàn)方式:
-
視圖層次結(jié)構(gòu)
-
自定義漸變動畫類,遵守
UIViewControllerAnimatedTransitioning
協(xié)議以實現(xiàn)具體的動畫效果:// ******************************************** // SCFlipAnimation.h #import <Foundation/Foundation.h> /** 遵守<UIViewControllerAnimatedTransitioning>協(xié)議的自定義漸變動畫 */ @interface SCFlipAnimation : NSObject <UIViewControllerAnimatedTransitioning> /** 確定頁面的翻轉(zhuǎn)方向 */ @property (nonatomic, assign) BOOL dismissal; @end // ******************************************** // SCFlipAnimation.m #import "SCFlipAnimation.h" @implementation SCFlipAnimation - (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { // 獲取各自的視圖控制器 UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; // 獲取視圖 UIView *containerView = [transitionContext containerView]; UIView *fromView = fromVC.view; UIView *toView = toVC.view; // 添加 toView 到容器視圖 [containerView addSubview:toView]; // 設(shè)置 frames CGRect initialFrame = [transitionContext initialFrameForViewController:fromVC]; fromView.frame = initialFrame; toView.frame = initialFrame; // 構(gòu)建3D轉(zhuǎn)換需要的視角 CATransform3D transform = CATransform3DIdentity; transform.m34 = -1/CGRectGetHeight(initialFrame); containerView.layer.sublayerTransform = transform; // 翻轉(zhuǎn)方向 CGFloat direction = self.dismissal ? -1.0 : 1.0; // toView 反向翻轉(zhuǎn) pi/2 toView.layer.transform = CATransform3DMakeRotation(-direction * M_PI_2, 1, 0, 0); // 關(guān)鍵幀動畫 [UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:0 animations:^{ // First half is rotating in [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{ // fromView 正向翻轉(zhuǎn) pi/2 fromView.layer.transform = CATransform3DMakeRotation(direction * M_PI_2, 1, 0, 0); }]; [UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{ // toView 還原 toView.layer.transform = CATransform3DMakeRotation(0, 1, 0, 0); }]; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { return 1.0; } @end
-
First View Controller,遵守
UIViewControllerTransitioningDelegate
協(xié)議@interface SCViewController () <UIViewControllerTransitioningDelegate> { SCFlipAnimation *_flipAnimation; } @end @implementation SCViewController #pragma mark - Lifecycle - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _flipAnimation = [SCFlipAnimation new]; } #pragma mark - - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([segue.destinationViewController isKindOfClass:[SCModalViewController class]]) { SCModalViewController *vc = (SCModalViewController *)segue.destinationViewController; } } #pragma mark - UIViewControllerTransitioningDelegate // 彈出模態(tài)視圖控制器 // 返回遵守 UIViewControllerAnimatedTransitioning 協(xié)議的實例 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { _flipAnimation.dismissal = NO; return _flipAnimation; } // 消失模態(tài)視圖控制器 // 返回遵守 UIViewControllerAnimatedTransitioning 協(xié)議的實例 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { _flipAnimation.dismissal = YES; return _flipAnimation; }
-
Model View Controller 實現(xiàn)按鈕方法
// 使模態(tài)視圖控制器消失 - (IBAction)handleDismissPressed:(id)sender { [self dismissViewControllerAnimated:YES completion:NULL]; }
Interactive transitioning - 交互式轉(zhuǎn)換
用手勢上滑、下滑操作實現(xiàn)視圖轉(zhuǎn)換
-
自定義
UIPercentDrivenInteractiveTransition
子類對象,用于手勢處理// ******************************************** // SCFlipAnimationInteractor.h #import <UIKit/UIKit.h> #import "SCInteractiveTransitionViewControllerDelegate.h" @interface SCFlipAnimationInteractor : UIPercentDrivenInteractiveTransition @property (nonatomic, strong, readonly) UIPanGestureRecognizer *gestureRecogniser; @property (nonatomic, assign, readonly) BOOL interactionInProgress; // 實現(xiàn) SCInteractiveTransitionViewControllerDelegate 自定義交互式轉(zhuǎn)場協(xié)議的任意視圖控制器 @property (nonatomic, weak) UIViewController<SCInteractiveTransitionViewControllerDelegate> *presentingVC; @end // ******************************************** // SCFlipAnimationInteractor.m #import "SCFlipAnimationInteractor.h" @interface SCFlipAnimationInteractor () @property (nonatomic, strong, readwrite) UIPanGestureRecognizer *gestureRecogniser; @property (nonatomic, assign, readwrite) BOOL interactionInProgress; @end @implementation SCFlipAnimationInteractor - (instancetype)init { self = [super init]; if (self) { self.gestureRecogniser = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; } return self; } #pragma mark - Gesture recognition // 手勢操作的方法 - (void)handlePan:(UIPanGestureRecognizer *)pgr { CGPoint translation = [pgr translationInView:pgr.view]; // 手勢百分比,用百分比的方式來激活指定的動畫過程。 CGFloat percentage = fabs(translation.y / CGRectGetHeight(pgr.view.bounds)); switch (pgr.state) { case UIGestureRecognizerStateBegan: { // 手勢開始,執(zhí)行翻轉(zhuǎn)動畫 self.interactionInProgress = YES; // 向目標(biāo)對象傳達(dá)執(zhí)行動畫的命令 [self.presentingVC proceedToNextViewController]; break; } case UIGestureRecognizerStateChanged: { // 手勢進(jìn)行中,更新翻轉(zhuǎn)進(jìn)度 [self updateInteractiveTransition:percentage]; break; } case UIGestureRecognizerStateEnded: { // 手勢結(jié)束,分進(jìn)度值處理 if(percentage < 0.5) { // 1.進(jìn)度小于0.5,取消轉(zhuǎn)場動畫 [self cancelInteractiveTransition]; } else { // 2.進(jìn)度大于0.5,自動完成轉(zhuǎn)場動畫 [self finishInteractiveTransition]; } self.interactionInProgress = NO; break; } case UIGestureRecognizerStateCancelled: { // 取消手勢 [self cancelInteractiveTransition]; self.interactionInProgress = NO; break; } default: break; } } @end
自定義交互協(xié)議
#import <Foundation/Foundation.h>
// 1.該協(xié)議的作用:
// 將手勢與目標(biāo)視圖相關(guān)聯(lián),手勢開始時,自定義的手勢處理類需要向目標(biāo)視圖發(fā)送執(zhí)行視圖轉(zhuǎn)場的命令。
// 2.為什么要將手勢與目標(biāo)視圖關(guān)聯(lián)呢?
// 因為手勢是添加到 window 上的,這樣就可以響應(yīng)預(yù)期的行為。
// 3.為什么手勢要添加到 window 上而不是視圖上?
// 因為動畫發(fā)生的時候,視圖控制器中的視圖將會被移除,因此手勢就沒有辦法響應(yīng)行為了。
@protocol SCInteractiveTransitionViewControllerDelegate <NSObject>
- (void)proceedToNextViewController;
@end
?
-
First View Controller
// ******************************************** // SCViewController.h #import <UIKit/UIKit.h> @interface SCViewController : UIViewController @end // ******************************************** // SCViewController.m #import "SCViewController.h" #import "SCModalViewController.h" #import "SCFlipAnimationInteractor.h" #import "SCFlipAnimation.h" #import "SCInteractiveTransitionViewControllerDelegate.h" @interface SCViewController () <SCInteractiveTransitionViewControllerDelegate, UIViewControllerTransitioningDelegate> { SCFlipAnimationInteractor *_animationInteractor; SCFlipAnimation *_flipAnimation; } @end @implementation SCViewController #pragma mark - Lifecycle - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _animationInteractor = [SCFlipAnimationInteractor new]; _flipAnimation = [SCFlipAnimation new]; } - (void)viewDidAppear:(BOOL)animated { // 添加手勢識別器到window對象中。 // 這是因為動畫發(fā)生的時候,視圖控制器中的視圖將會被移除,因此手勢就沒有辦法響應(yīng)行為了。 // 把它添加到window上面,將會確保我們期待的行為。 if (![self.view.window.gestureRecognizers containsObject:_animationInteractor.gestureRecogniser]) { [self.view.window addGestureRecognizer:_animationInteractor.gestureRecogniser]; } // 在第一頁中,交互的接收者指向 self 自身 _animationInteractor.presentingVC = self; } #pragma mark - - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if([segue.destinationViewController isKindOfClass:[SCModalViewController class]]) { // Set the delegate SCModalViewController *vc = (SCModalViewController *)segue.destinationViewController; vc.transitioningDelegate = self; // 模態(tài)視圖的手勢交互器也指向這個類的手勢交互器(弱引用) // 也就是說,這兩個視圖的手勢交互器指向的是同一個對象 vc.interactor = _animationInteractor; } } #pragma mark - SCInteractiveTransitionViewControllerDelegate - (void)proceedToNextViewController { [self performSegueWithIdentifier:@"displayModal" sender:self]; } #pragma mark - UIViewControllerTransitioningDelegate // 彈出模態(tài)視圖控制器 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { _flipAnimation.dismissal = NO; return _flipAnimation; } // 消失模態(tài)視圖控制器 - (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { _flipAnimation.dismissal = YES; return _flipAnimation; } // 交互式轉(zhuǎn)場方法1:展示 // 返回一個實現(xiàn) UIViewControllerInteractiveTransitioning 協(xié)議的對象 - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id<UIViewControllerAnimatedTransitioning>)animator { return _animationInteractor.interactionInProgress ? _animationInteractor : nil; } // 交互式轉(zhuǎn)場方法2:消失 // 返回一個實現(xiàn) UIViewControllerInteractiveTransitioning 協(xié)議的對象 - (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator { return _animationInteractor.interactionInProgress ? _animationInteractor : nil; } @end
-
Model View Controller
// ******************************************** // SCModalViewController.h #import <UIKit/UIKit.h> #import "SCInteractiveTransitionViewControllerDelegate.h" #import "SCFlipAnimationInteractor.h" @interface SCModalViewController : UIViewController <SCInteractiveTransitionViewControllerDelegate> - (IBAction)handleDismissPressed:(id)sender; @property (nonatomic, weak) SCFlipAnimationInteractor *interactor; @end // ******************************************** // SCModalViewController.m #import "SCModalViewController.h" @implementation SCModalViewController #pragma mark - Lifecycle - (void)viewDidAppear:(BOOL)animated { // 重新設(shè)置哪一個視圖控制器是交互式轉(zhuǎn)場的接收者 // 在模態(tài)頁面中,交互的接收者又重新設(shè)置為模態(tài)頁面自身 self.interactor.presentingVC = self; } #pragma mark - IBActions // 使模態(tài)視圖控制器消失 - (IBAction)handleDismissPressed:(id)sender { [self dismissViewControllerAnimated:YES completion:NULL]; } #pragma mark - SCInteractiveTransitionViewControllerDelegate - (void)proceedToNextViewController { [self dismissViewControllerAnimated:YES completion:NULL]; } @end
The End.