Skip to content

View Controller转场

戴铭 edited this page Mar 11, 2015 · 2 revisions

UINavigationControllerDelegate中相关方法

- (id<UIViewControllerAnimatedTransitioning>)
     navigationController:(UINavigationController *)navigationController
     animationControllerForOperation:(UINavigationControllerOperation)operation
     fromViewController:(UIViewController*)fromVC
     toViewController:(UIViewController*)toVC
{
     if (operation == UINavigationControllerOperationPush) {
     //可以给每个转场创建新的animator对象,或者共用同一个animator     
     return self.animator;
     }
     return nil;
}

创建一个自定义的动画类

示例代码下载地址:https://github.com/objcio/issue5-view-controller-transitions

//实现这个协议
@interface Animator : NSObject <UIViewControllerAnimatedTransitioning>

@end

//动画持续时间
- (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
{
     return 0.25;
}

//动画执行效果
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
     UIViewController* toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
     UIViewController* fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
     [[transitionContext containerView] addSubview:toViewController.view];
     toViewController.view.alpha = 0;

     [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{
          fromViewController.view.transform = CGAffineTransformMakeScale(0.1, 0.1);
          toViewController.view.alpha = 1;
     } completion:^(BOOL finished) {
          fromViewController.view.transform = CGAffineTransformIdentity;
          [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
     }];
}

//实现交互式转场动画,只需要覆盖另一个UINavigationControllerDelegate的方法
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController*)navigationController
     interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>)animationController
{
     //返回的是UIPercentDrivenInteractionTransition类的一个实例 
     return self.interactionController;
}

//创建一个拖动手势Pan Rcognizer
if (panGestureRecognizer.state == UIGestureRecognizerStateBegan) {
    //当用户从屏幕右半部分开始触摸时,把下次动画效果设置为交互式
     if (location.x > CGRectGetMidX(view.bounds)) {
          navigationControllerDelegate.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init];
     [self performSegueWithIdentifier:PushSegueIdentifier sender:self];
     }
} else if (panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
    //根据用户手指拖动的距离计算一个百分比,切换的动画效果也跟这个百分比走。
     CGFloat d = (translation.x / CGRectGetWidth(view.bounds)) * -1;
     [interactionController updateInteractiveTransition:d];
} else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
     //根据用户手势的停止状态来判断该操作是结束还是取消
     if ([panGestureRecognizer velocityInView:view].x < 0) {
          [interactionController finishInteractiveTransition];
     } else {
          [interactionController cancelInteractiveTransition];
     }
     navigationControllerDelegate.interactionController = nil;
}

用GPUImage做转场动画

示例代码地址:https://github.com/FangYiXiong/ViewControllerTransitionsDemo/tree/master/issue5-demo2%EF%BC%88GPUImage%EF%BC%89 实现的效果是两个view controller像素化,然后相互消融在一起。 先创建一个自定义类,实现UIViewControllerAnimatedTransitioning 和 UIViewControllerInteractiveTransitioning两个协议。将图片一次性加载到GPU中。直接用OpenGL画图使用GPUImage封装好的接口。

@interface GPUImageAnimator : NSObject
     <UIViewControllerAnimatedTransitioning,
     UIViewControllerInteractiveTransitioning>

@property (nonatomic) BOOL interactive;
@property (nonatomic) CGFloat progress;

- (void)finishInteractiveTransition;
- (void)cancelInteractiveTransition;

@end

创建滤镜链filter chain也很直观。GPUImage没有提供动画效果,可以使用CADisplayLink完成渲染一帧就更新下一滤镜的动态滤镜效果。

self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(frame:)];
[self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
//在frame方法中,可以根据时间来更新动画,相应的更新滤镜:
- (void)frame:(CADisplayLink*)link
{
     self.progress = MAX(0, MIN((link.timestamp - self.startTime) / duration, 1));
     self.blend.mix = self.progress;
     self.sourcePixellateFilter.fractionalWidthOfAPixel = self.progress *0.1;
     self.targetPixellateFilter.fractionalWidthOfAPixel = (1- self.progress)*0.1;
     [self triggerRenderOfNextFrame];
}

GPUImage开源库地址:https://github.com/BradLarson/GPUImage

Clone this wiki locally