码迷,mamicode.com
首页 > 移动开发 > 详细

iOS开发——圆形过渡动画

时间:2015-02-19 17:31:03      阅读:340      评论:0      收藏:0      [点我收藏+]

标签:ios开发   swift   动画   

前言

在一款新的app——Ping中,用户可以订阅自己感兴趣的主题,该应用会向用户推送相关的文章或段落。该应用在视图的切换时采用了一个非常炫酷的动画效果,如下图所示:
技术分享
现在我们就来实现这一效果。总的来说,所用到的知识点有:
1、使用代理UIViewControllerAnimatedTransitioning实现控制器间的自定义动画
2、使用UIShapeLayer创建一个特定形状的层
3、配合mask效果实现视图的切换
4、使用手势与UIPercentDrivenInteractiveTransition实现可控过程的交互。

实现思路

结构分析

首先,这里的切换效果是控制器间的过渡效果,并且是基于UINavigationContoller的,因为我们需要通过导航控制器的代理来实现。8.0以后,UINavigationController提供了一个代理UINavigationControllerDelegate可以让我们对控制器间的pop、push过程进行控制,代理中有一个方法:

func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?

这个方法的返回值是一个实现了UIViewControllerAnimatedTransitioning协议的对象,这是我们项目中另外一个协议。用它可以控制动画时间、编写自定义动画流程、对动画完成后的资源进行回收处理等。

总的来说,我们需要的东西有:
1、两个控制器供切换
2、一个UINavigationControllerDelegate对象控制视图控制器的切换流程
3、一个UIViewControllerAnimatedTransitioning对象提供具体的动画

功能分析

当另一个控制器将要展示内容时,从上图可以看到,首先从右上角出现一个圆形,圆形不断放大,将要展示的内容就在圆形中,圆形之外依然是原来控制器的内容。动画过程中,这个圆形不断放大,随着它的增大,新内容也就慢慢出来了。换句话说,这个圆形的作用就是展示范围内的,屏蔽范围外的。
技术分享
这里用遮挡层来实现是再好不过了,mask属性就决定着这一遮挡过程。当然了mask需要一个CALayer对象,这里也说过,这个过程中出现的是圆形,对于特定的形状,CAShapeLayer就能方便的解决问题了。

具体实现

有了思路,实现起来就简单了。首先要准备好两个控制器,基本的要求就是提供一个按钮可以用来视图切换即可:
技术分享
这里从左到右依次是UINavigationController、ViewController、ViewController,右上角的按钮(圆角效果)用来切换,图片的位置乱是由于使用了auto layout,加上只有一张图片,懒得调了:]

第一步,动画的实现
既然动画的具体过程是由UIViewControllerAnimatedTransitioning对象来实现的,那么我们先创建这个对象。UIViewControllerAnimatedTransitioning是一个协议,该协议中有两个必须方法和一个可选方法:

func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval
func animateTransition(transitionContext: UIViewControllerContextTransitioning)
optional func animationEnded(transitionCompleted: Bool)

按照上面说的,我们单独建一个类,让新类实现该协议,并编写这三个方法:

class CircleTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    //context contains fromViewController、toViewController、containView etc.
    //We keep a reference here to get the context during the animation
    weak var transitionContext: UIViewControllerContextTransitioning?

    //duration time
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
        return 0.5
    }

    //animation progress
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        //get the context
        self.transitionContext = transitionContext
        //get other important variable
        var containView = transitionContext.containerView()
        var fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as ViewController
        var toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as ViewController
        //button here means the button that controls the changing progress in fromViewController
        //we will let the circle start from button‘s center
        var button = fromVC.button

        containView.addSubview(toVC.view)

        //calculate some values
        let circlePathInitial = UIBezierPath(ovalInRect: button.frame)
        //just big enough is OK, too. Because here we just need to make sure the mask can cover all view
        let extremePoint = CGPoint(x: button.center.x, y: button.center.y - toVC.view.bounds.size.height)
        let radius = sqrt(extremePoint.x * extremePoint.x + extremePoint.y * extremePoint.y)
        let circlePathFinal = UIBezierPath(ovalInRect: CGRectInset(button.frame, -radius, -radius))

        //create shape layer
        let shapeLayer = CAShapeLayer()
        shapeLayer.path = circlePathFinal.CGPath
        toVC.view.layer.mask = shapeLayer

        //create animation and add it
        let maskAnimation = CABasicAnimation(keyPath: "path")
        maskAnimation.fromValue = circlePathInitial.CGPath
        maskAnimation.toValue = circlePathFinal.CGPath
        maskAnimation.duration = self.transitionDuration(transitionContext)
        maskAnimation.delegate = self
        shapeLayer.addAnimation(maskAnimation, forKey: "path")
    }

    override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
        //end the animation
        self.transitionContext?.completeTransition(!self.transitionContext!.transitionWasCancelled())
        //remove the mask at fromViewController
        self.transitionContext?.viewControllerForKey(UITransitionContextFromViewControllerKey)?.view.layer.mask = nil

    }


}

注释中已经解释得非常详细了,这里从上下文中获取两个控制器,基于此创建CAShapeLayer,通过设置起始路径(主要是半径不同),并赋值给动画的起始属性,来完成这个流程。

第二步,动画的控制
接下来我们需要UINavigationControllerDelegate对象来控制上面完成的动画,依然新建一个类,实现该协议,并编写相关方法:

class NavigationControllerDelegate: NSObject, UINavigationControllerDelegate {

    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CircleTransitionAnimator()
    }

}

第三步。连接
最后,为我们跟控制器(导航控制器)的delegate赋值上面这个类的一个对象即可。这个过程可以在storyboard中完成,拖一个Object对象到UINavigationController中,将类改为NavigationControllerDelegate,然后把导航控制器的delegate拖到该对象上即可
技术分享

现在已经可以看到效果了,并且跟Ping类似:
技术分享

手势操作

这样我们的动画就完成了。接下来,我们来把按钮给“消灭”掉。
随着手势的流行,我们可以用手势来实现很多操作,并且这些过程往往是可控的,典型的例子就是前一阵很火的抽屉菜单。
现在我们为UINavigationController添加一个pan手势,让视图切换效果受用户拖动控制。
UINavigationControllerDelegate提供了一个方法用来返回“交互过程”:

func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?

UIViewControllerInteractiveTransitioning是一个代理,UIPercentDrivenInteractiveTransition便是iOS为我们提供的一个实现了这一代理的类,该类可以按比例更新视图切换过程、直接完成切换、取消切换……因此,我们首先需要一个该类对象。另外,在UINavigationControllerDelegate中,我们也要获得UINavigationController的引用,因此声明两个变量:

var interactionController: UIPercentDrivenInteractiveTransition?
@IBOutlet weak var navigationVC: UINavigationController?

添加Pan手势:

    override func awakeFromNib() {
        super.awakeFromNib()
        let panGR: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: Selector("handlePan:"))
        self.navigationVC?.view.addGestureRecognizer(panGR)
    }

核心是手势处理代码,思路非常简单,根据用户滑动的偏移值决定切换的百分比:

    func handlePan(recognizer: UIPanGestureRecognizer) {

        var transition = recognizer.translationInView(self.navigationVC!.view)
        var progress = fabs(transition.x) / self.navigationVC!.view.bounds.size.width

        switch recognizer.state {
        case .Began:
            self.interactionController = UIPercentDrivenInteractiveTransition()
            if self.navigationVC?.viewControllers.count > 1 {
                self.navigationVC?.popViewControllerAnimated(true)
            } else {
                self.navigationVC!.topViewController.performSegueWithIdentifier("PushSegue", sender: nil)
            }

        case .Changed:
            self.interactionController?.updateInteractiveTransition(progress)

        case .Ended:

            if fabs(recognizer.velocityInView(recognizer.view).x) > 0 {
                self.interactionController?.finishInteractiveTransition()
            } else {
                self.interactionController?.cancelInteractiveTransition()
            }
            self.interactionController = nil

        default:
            self.interactionController?.cancelInteractiveTransition()
            self.interactionController = nil
        }
    }

最后,在代理方法中返回我们创建的这个UIPercentDrivenInteractiveTransition对象:


    func navigationController(navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return self.interactionController
    }

这样手势操作就完成了,效果还是不错的
技术分享

参考资料

本文是对Raywenderlich的文章

http://www.raywenderlich.com/86521/how-to-make-a-view-controller-transition-animation-like-in-the-ping-app

的学习总结,部分图片也来自该文章。特此感谢~

iOS开发——圆形过渡动画

标签:ios开发   swift   动画   

原文地址:http://blog.csdn.net/u013604612/article/details/43883029

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!