自定义Controller转场动画

Posted duzhaoquan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义Controller转场动画相关的知识,希望对你有一定的参考价值。

当你想使用一个自定义的模态表示类型来呈现一个视图控制器时,设置它的modalPresentationStyle属性为custom,并将一个符合这个协议的对象分配给它的transitioningDelegate属性。当你展示那个视图控制器时,UIKit查询你的转换代理当视图控制器进入位置时使用的对象。

//一 创建控制器
class TransitionViewController: UIViewController {

    var endVelocity: CGPoint?
    let imageView = UIImageView(frame: CGRect(x: 25, y: 25, width: 150, height: 150))
    init() {
        super.init(nibName: nil, bundle: nil)
        self.modalPresentationStyle = .custom
        self.transitioningDelegate = self
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .orange

        imageView.image = UIImage(named: "666")
        view.addSubview(imageView)
        imageView.isUserInteractionEnabled = true
        let tap1 = UITapGestureRecognizer(target: self, action: #selector(clickImage))
        imageView.addGestureRecognizer(tap1)
        
        let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
        view.addGestureRecognizer(pan)
        
    }
   
    @objc func clickImage(){
        self.dismiss(animated: true, completion: nil)
    }
    
    @objc func handlePan(gesture: UIPanGestureRecognizer) {
           if gesture.state == .began {
               
           } else if gesture.state == .changed {
              let translation = gesture.translation(in: view.superview)
              let center = view.center
               UIView.animate(withDuration: 0.1) {
                   self.view.center = .init(x: center.x + translation.x, y: center.y + translation.y)
                   gesture.setTranslation(.zero, in: self.view.superview)
               }
               
           } else {
            let screenCenter = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2)
               let viewCenter = view.center
               
               let dis = sqrt(pow((screenCenter.x - viewCenter.x), 2) + pow((screenCenter.y - viewCenter.y), 2))
               let vl = gesture.velocity(in: view.superview)
               let v = sqrt(pow(vl.x,2)+pow(vl.y, 2))
               if dis >= 120 || v > 500 {
                   if v > 500 {
                       endVelocity = gesture.velocity(in: view.superview)
                   }
                   dismiss(animated: true, completion: nil)
               } else {
                   UIView.animate(withDuration: 0.3) {
                       self.view.center = screenCenter
                   }
               }
           }
       }
   
}

///第二步 实现代理协议

extension TransitionViewController : UIViewControllerTransitioningDelegate{
    
    //模态进入时的动画
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return  DqPresentAnimation()
    }
   //模态推出时的动画
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return DqDismissAnimation()
    }
    //从模态开始到推出的一个控制器,比推出的控制器生命周期更长
    
    /// - Parameters:
    ///   - presented: 已推出的控制器
    ///   - presenting: 正在推出的控制器
    ///   - source: 原始控制器
    /// - Returns: 返回自己创建的过程控制器
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return DqPresentationController(presentedViewController: presented, presenting: presenting)
    }
    
}

/// 第三步 实现Present和Dismiss动画协议

class DqPresentAnimation: NSObject,UIViewControllerAnimatedTransitioning {
    
    //动画时间
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.6
    }
    //具体动画
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
        let toViewController = transitionContext.viewController(forKey: .to)
        let containerView = transitionContext.containerView

        let toView = toViewController!.view!
        
        let finalFrame = transitionContext.finalFrame(for: toViewController!)
        toView.frame = finalFrame

        containerView.addSubview(toView)
        
        toView.center = .init(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2 + 50)
        toView.alpha = 0
        toView.layer.masksToBounds = true
        toView.layer.cornerRadius = 20
        toView.layer.masksToBounds = false

        toView.isUserInteractionEnabled = true
        toView.layer.shadowRadius = 20
        let path = UIBezierPath(roundedRect: toView.bounds.insetBy(dx: 20, dy: 20), cornerRadius: 20)
        toView.layer.shadowPath = path.cgPath
        toView.layer.shadowOffset = .init(width: 0, height: 25)
        toView.layer.shadowOpacity = 0.6

        let animator = UIDynamicAnimator()
            animator.removeAllBehaviors()
        let snapBehavior = UISnapBehavior(item: toViewController!.view, snapTo: .init(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2))
        snapBehavior.damping = 0.1
        animator.addBehavior(snapBehavior)
        
        UIView.animate(withDuration: 0.6, animations: {
            toView.alpha = 1
        }) { (finish) in
            animator.removeAllBehaviors()
            transitionContext.completeTransition(true)
        }
    }
    
}

//Dismiss动画
class DqDismissAnimation: NSObject,UIViewControllerAnimatedTransitioning {
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.6
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let fromViewController = transitionContext.viewController(forKey: .from) as! TransitionViewController
        let toViewController = transitionContext.viewController(forKey: .to)
        let containerView = transitionContext.containerView
        
        let fromView = fromViewController.view!
        let toView = toViewController!.view!
        //系统会给view改为不可交互,需要手动打开交互
        toView.isUserInteractionEnabled = true
        
        containerView.addSubview(fromView)
        
        if let velocity = fromViewController.endVelocity {
                
                let magnitude = sqrt((velocity.x * velocity.x) + (velocity.y * velocity.y))
                let slideMultiplier = magnitude / 100
                print("magnitude: (magnitude), slideMultiplier: (slideMultiplier)")
                  
                // 2
                let slideFactor = 1 * slideMultiplier     //Increase for more of a slide
                // 3
                var finalPoint = CGPoint(x:fromView.center.x + (velocity.x * slideFactor),
                                           y:fromView.center.y + (velocity.y * slideFactor))
                // 4
            finalPoint.x = min(max(finalPoint.x, 0), UIScreen.main.bounds.width + 150)
                finalPoint.y = min(max(finalPoint.y, 0), UIScreen.main.bounds.height + 150)
                  
                // 5
                UIView.animate(withDuration: 0.2, animations: {
                    fromView.center = finalPoint
                    fromView.alpha = 0
                },
                    completion: { finished in
                        if finished {
                            transitionContext.completeTransition(true)
                        }
                })
            } else {
                UIView.animate(withDuration: 0.2, animations: {
                    fromView.center = .init(x: fromView.center.x, y: UIScreen.main.bounds.height*1.5)
                },
                    completion: { finished in
                        if finished {
                            transitionContext.completeTransition(true)
                        }
                })
            }
        }
    
}

//第四步 UIPresentationController

/// For custom modal transition styles, you can provide a UIPresentationController object in addition to the animator objects. The system creates your presentation controller before presenting the view controller and keeps a reference to that object until the view controller is dismissed. Because its existence extends beyond the lifespan of either animator object, you can use the presentation controller to coordinate aspects of the presentation or dismissal process that would be difficult to do otherwise. For example, if your custom transition style involves displaying a separate shadow view as a backdrop to the view controller’s content, the presentation controller can create the shadow view and show it and hide it at the appropriate times.
class DqPresentationController: UIPresentationController {
    
    var blur:UIVisualEffect!
    var blurView:UIVisualEffectView!
    
    
    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        blur = UIBlurEffect(style: UIBlurEffect.Style.dark)
        blurView = UIVisualEffectView()
    }
    
    override var frameOfPresentedViewInContainerView: CGRect{
        return CGRect(x: (kScreenWith - 200)/2, y: (kScreenHieght - 200)/2, width: 200, height: 200)
    }
    
    override func containerViewDidLayoutSubviews() {
         if let containerView = containerView {
           blurView.frame = containerView.bounds
       } else {
            blurView.frame = UIScreen.main.bounds
       }
    }
    override func presentationTransitionWillBegin() {
        containerView?.addSubview(blurView)
        UIView.animate(withDuration: 0.6, animations: {
            self.blurView.effect = self.blur
        }) { (finish) in
            
        }
        
    }
    
    override func dismissalTransitionWillBegin() {
        UIView.animate(withDuration: 0.6, animations: {
            self.blurView.effect = nil
        }) { (finish) in
            self.blurView.isHidden = true
        }
    }
    
    override func dismissalTransitionDidEnd(_ completed: Bool) {
        blurView.removeFromSuperview()
    }
    
}

 

以上是关于自定义Controller转场动画的主要内容,如果未能解决你的问题,请参考以下文章

自定义模态视图的转场动画

swift自定义转场动画(比较有难度)

第六十五篇iOS7自定义转场动画

Android的Fragment的自定义转场动画

UINavigationController 自定义转场动画(模仿淘宝App跳转)

iOS 自定义转场动画