轉載大神? ? http://www.cocoachina.com/ios/20150401/11459.html
? ? ? 自iOS7之后泻拦,引進了新的API來構造UIViewController之間的轉場動畫忽媒,經(jīng)過幾天的研究,終于做出了一個小Damo架曹,來粗淺談談。這幾個API如下:
<1.>UIViewControllerAnimatedTransitioning? 動畫協(xié)議<2>.UIViewControllerInteractiveTransitioning? 交互協(xié)議<3>.UIViewControllerContextTransitioning? ? ? 上下文協(xié)議<4>.UIPercentDrivenInteractiveTransition? ? ? ? 遵守? ?
? ? ? 協(xié)議的一個官方類之所以官方給出的API是協(xié)議而不是類別绑雄,給出的說法是為了靈活性,你可以在ViewController里面直接寫罗珍,也可以直接另寫一個類封裝起來脚粟。進入正文:
? ? ? 1、? 這個類負責動畫扣唱,繼承自NSObject画舌,遵守UIViewControllerAnimatedTransitioning協(xié)議已慢,記得導入UIKit,如下
? ? ? @interface PopAnimation : NSObject<UIViewControllerAnimatedTransitioning>
? ? ? @end
? ? ? 在.m文件中實現(xiàn)協(xié)議其中的兩個方法: ? ? ??
? ? ? -(NSTimeInterval)transitionDuration:(id)transitionContext?
{
? ? ? //這個方法返回動畫執(zhí)行的時間
? ? ? return 0.25;
}
? ? ? -(void)animateTransition:(id)transitionContext
?{
? ? ? /** transitionContext你可以看作是一個工具朋腋,用來獲取一系列動畫執(zhí)行相關的對象膜楷,并且通知系統(tǒng)動畫是否完成等功能。*/
? ? ?/**? 獲取動畫來自的那個控制器 */
? ? ? ? ? ? UIViewController *fromViewController = [transitionContext ? ? viewControllerForKey:UITransitionContextFromViewControllerKey];
? ? ?/**? 獲取轉場到的那個控制器 */
? ? ? ? ? ?UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
? ? ? /**? 轉場動畫是兩個控制器視圖時間的動畫穷绵,需要一個containerView來作為一個“舞臺”仲墨,讓動畫執(zhí)行揍障。 */
? ? ? UIView *containerView = [transitionContext containerView]; ?
? ? ? [containerView insertSubview:toViewController.view belowSubview:fromViewController.view];
? ? ? NSTimeInterval duration = [self transitionDuration:transitionContext];
? ? ? /**//? ? *? 執(zhí)行動畫,我們讓fromVC的視圖移動到屏幕最右側//? ? */ ? ? ??
? ? ? [UIView animateWithDuration:duration? animations:^{ ? ? ? ? ??
? ? ? ? ? ? ?fromViewController.view.transform =?
? ? ? ? ? ? ?CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0);}completion:^(BOOL finished) {??
? ? ? ? ? ? /**//? ? ? ? *? 當你的動畫執(zhí)行完成癌蚁,這個方法必須要調(diào)用,否則系統(tǒng)會認為你的其余任何操作都在動畫執(zhí)行過程中碘梢。//? ? ? ? */
?? ? ? ?[transitionContext completeTransition:!transitionContext.transitionWasCancelled];
?? ?}];
也可以使用動畫
? ? ? ?_transitionContext = transitionContext;
? ? ? //? ? ----------------pop動畫一------------------------- ? ? ??
? ? ? ? [UIView beginAnimations:@"View Flip" context:nil];
? ? ? ? [UIView setAnimationDuration:duration];[UIView setAnimationDelegate:self];
? ? ? ? [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:containerView cache:YES];
? ? ? ? [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:)];
? ? ? ? [UIView commitAnimations];//提交UIView動畫
? ? ? ? [containerView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
? ? ? ? ?第一個方法返回的是動畫時間痘系,不做多言饿自。第二個方法是動畫的具體執(zhí)行昭雌,方法的參數(shù)transitionContext遵守了UIViewControllerContextTransitioning協(xié)議健田,所以它包含了許多關于專場所需要的內(nèi)容,包括轉入ViewController和轉出Viewcontroller总放,還有動畫容器View--containerView等好爬。我們點進去UIViewControllerContextTransitioning協(xié)議,可以找到許多的屬性和方法炬搭,這些方法中最重要的幾個方法和意義如下:
? ? ? ? (UIView*)containerView;? //獲取容器View ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ? ? (void)completeTransition:(BOOL)didComplete;//通過此參數(shù)獲知動畫是否結束? ? ? ? ? ? ? ? ?
? ? ? ? (UIViewController*)viewControllerForKey:(NSString*)key;? //獲取轉入宫盔、轉出VC
? ? ? ? (CGRect)initialFrameForViewController:(UIViewController*)vc? //獲取動畫前VC的frame
? ? ? ? (CGRect)finalFrameForViewController:(UIViewController*)vc;? ? //獲取動畫后VC的frame
? ? ? ? (push的基類也和pop一樣享完,只是具體動畫效果代碼的不同)
? ? ? ? 另外,我們需要返回一個遵守了UIViewControllerInteractiveTransitioning協(xié)議的對象(提示一下彼绷,這兩個協(xié)議容易混淆倒源,要注意區(qū)分,一個是負責動畫热某,一個是負責交互過程),蘋果已經(jīng)有一個類專門處理這個功能筹吐,它叫UIPercentDrivenInteractiveTransition秘遏,當然你也可以自定義一個這樣的類。我們可以這樣理解它的作用:
? ? ? ?前面在方法1中返回的動畫洋侨,會在執(zhí)行的過程中被系統(tǒng)分解以用于用戶交互倦蚪,這個交互過程的動畫完成度就由它來調(diào)控。
? ? ? 下面我們來看一下如何使用它裁僧。(為了讓控制器視圖拖動慕购,我們給控制器的視圖加了一個拖動手勢,在拖動方法里我們對這個對象進行操作)定義一個類获洲,NavigationInteractiveTransition 可训,實現(xiàn):
? ? ?.h中
? ? ?@class UIViewController,UIPercentDrivenInteractiveTransition;
? ? ?@interface NavigationInteractiveTransition : NSObject
? ? ? -(instancetype)initWithViewController:(UIViewController *)vc;- ?
? ? ? -(void)handleControllerPop:(UIPanGestureRecognizer *)recognizer;
? ? ? -(UIPercentDrivenInteractiveTransition *)interactivePopTransition;
@end
? ? .m中設置導航控制器的delegate為這個自定義的類
? ? ? @interface NavigationInteractiveTransition ()
? ? ? @property (nonatomic, weak) UINavigationController *vc;
? ? ? @property (nonatomic, strong) UIPercentDrivenInteractiveTransition *interactivePopTransition;
? ? ? @end
? ? ? @implementation NavigationInteractiveTransition ??
? ? ? -(instancetype)initWithViewController:(UIViewController *)vc
{
? ? ? self = [super init];
? ? ? if (self) {??
? ? ? ? ? ? ? ? ? ?self.vc = (UINavigationController *)vc;? ?
? ? ? ? ? ? ? ? ? ? self.vc.delegate = self;
? ? ? ?}
? ? ? ? return self;
}
? ? ? ?添加手勢代理/*? 我們把用戶的每次Pan手勢操作作為一次pop動畫的執(zhí)行 */
? ? ? ?-(void)handleControllerPop:(UIPanGestureRecognizer *)recognizer {
? ? ? ?/*? interactivePopTransition就是我們說的方法2返回的對象握截,我們需要更新它的進度來控制Pop動畫的流程,我們用手指在視圖中的位置與視圖寬度比例作為它的進度固歪。 */
? ? ? ?CGFloat progress = [recognizer translationInView:recognizer.view].x / recognizer.view.bounds.size.width;
? ? ? ? ?/**? 穩(wěn)定進度區(qū)間胯努,讓它在0.0(未完成)~1.0(已完成)之間 */
? ? ? ? ? progress = MIN(1.0, MAX(0.0, progress));
? ? ? ? ? if (recognizer.state == UIGestureRecognizerStateBegan) {?
? ? ? ? ? ? /**? ? ? 手勢開始,新建一個監(jiān)控對象? ? */? ??
? ? ? ? ? ? ? ? ? ? ?self.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];??
? ? ? ? ? ? ?/**? ? ? 告訴控制器開始執(zhí)行pop的動畫? ? */??
? ? ? ? ? ? ? ? ? ? ? ?[self.vc popViewControllerAnimated:YES];
? ? ? ? ? ? ?}else if (recognizer.state == UIGestureRecognizerStateChanged) {??
? ? ? ? ? ? ? ? ? ? ? ?/**? ? ? 更新手勢的完成進度? ? */? ?
? ? ? ? ? ? ? ? ? ? ? ? [self.interactivePopTransition updateInteractiveTransition:progress];
? ? ? ? ? ? }else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {?
? ? ? ? ? ? ? ? ? ? ? ? ? /**? ? ? 手勢結束時如果進度大于一半蒲讯,那么就完成pop操作判帮,否則重新來過。? ? */? ?
? ? ? ? ? ? ? ? ? ? ? ? ? if (progress > 0.3) {??
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self.interactivePopTransition finishInteractiveTransition]; ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? }? ? else {?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?[self.interactivePopTransition cancelInteractiveTransition]; ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? ? ? ? ? ? self.interactivePopTransition = nil; ??
? ? ? ? ? ? ? ? }
}??
? ? ? ?然后初始化pop動畫類 ? ?
? ? ? ?-(id)navigationController:(UINavigationController *)navigationController? ? ? ? ? ? ? ? ? ? ? ? ? ? ? animationControllerForOperation:(UINavigationControllerOperation)operation? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? fromViewController:(UIViewController *)fromVC? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? toViewController:(UIViewController *)toVC {
? ? ? ? ? ?/**? 方法1中判斷如果當前執(zhí)行的是Pop操作悦昵,就返回我們自定義的Pop動畫對象晌畅。 */
? ? ? ? ? ?if (operation == UINavigationControllerOperationPop)? ? return [[PopAnimation alloc] init];return nil;
?}
? ? ? ? ? 最后,自定義一個集成UINavigationcontroller的類來使用
? ? ? ? ? #import "NavigationInteractiveTransition.h"
? ? ? ? ? @interface Nav ()@property (nonatomic, weak) UIPanGestureRecognizer *popRecognizer;
? ? ? ? ? @property (nonatomic, strong) NavigationInteractiveTransition *navT;
? ? ? ? ? @end
? ? ? ? ? @implementation Nav
? ? ? ? ? -(void)viewDidLoad
?{
? ? ? ? ? ?[super viewDidLoad];
? ? ? ? ? ?UIGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
? ? ? ? ? ?gesture.enabled = NO;
? ? ? ? ? ?UIView *gestureView = gesture.view;
? ? ? ? ? ?UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizer alloc] init];
? ? ? ? ? ? popRecognizer.delegate = self;popRecognizer.maximumNumberOfTouches = 1;
? ? ? ? ? ? [gestureView addGestureRecognizer:popRecognizer];
? ? ? ? ? ?_navT = [[NavigationInteractiveTransition alloc] initWithViewController:self];
? ? ? ? ? ? [popRecognizer addTarget:_navT action:@selector(handleControllerPop:)];
}
? ? ? ? ? - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer?
{
? ? ? ? ? ?/**? 這里有兩個條件不允許手勢執(zhí)行棋凳,1贫橙、當前控制器為根控制器;2、如果這個push疲迂、pop動畫正在執(zhí)行(私有屬性) */
? ? ? ? ? ?return self.viewControllers.count != 1 && ![[self valueForKey:@"_isTransitioning"] boolValue];
}
? ? ? ? ? 接下來就可以使用了,push和pop的方法一樣郑气,只是在實例化的時候注意區(qū)分
? ? ? ? ? ? -(id)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC {
/**
方法1中判斷如果當前執(zhí)行的是Pop操作尾组,就返回我們自定義的Pop動畫對象。
*/
if (operation == UINavigationControllerOperationPop)
return [[PopAnimation alloc] init];
else if (operation == UINavigationControllerOperationPush)
return [[PushAnimation alloc] init];
return nil;
}
常見問題:
經(jīng)常會遇到讳侨,橫向翻頁的scrollview和滑動手勢沖突跨跨,那么
1.首先自定義一個scrollView,比如:CustomScrollView勇婴,遵守協(xié)議嘱腥,然后在實現(xiàn)文件中寫如下代碼:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// 首先判斷otherGestureRecognizer是不是系統(tǒng)pop手勢
if ([otherGestureRecognizer.view isKindOfClass:NSClassFromString(@"UILayoutContainerView")]) {
// 再判斷系統(tǒng)手勢的state是began還是fail,同時判斷scrollView的位置是不是正好在最左邊
if (otherGestureRecognizer.state == UIGestureRecognizerStateBegan && self.contentOffset.x == 0) {
return YES;
}
}
return NO;
}
2.那個橫向滾動的scrollView繼承這個自定義scrollView橱脸,也就是CustomScrollView