自定义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转场动画的主要内容,如果未能解决你的问题,请参考以下文章