iOS 转场动画的简介

版权声明:转载请标注原文地址。邮箱[email protected] https://blog.csdn.net/Xoxo_x/article/details/83119917
有关转场动画的文章,多如牛毛,也有很多大神的文章,或是开源demo,我从中精简了主要流程,以及自己的开发经验,写了这篇文章,希望有所帮助。
早期的时候有一个Demo,在github上:
本想做一些转场动画的library,但是由于种种原因搁浅了。
本文也将结合这个Demo来做一些补充。

https://github.com/quinn0809/PresentTranslationDemo

一、CATranlation的转场动画

引用:iOS 动画 —— CATransition : https://www.jianshu.com/p/239cf81eb1eb
这个适合uiview的转场,或者简单的viewcontroller的转场,估计很难满足你的需求。

二、UIViewControllerTransitioningDelegateUINavigationControllerDelegate

这样讲:一个复杂的、自定义的、完整的转场动画,包括入场手势交互+入场动画+出场手势交互+出场动画

然而,大部分转场动画为:入场动画+出场动画
入场手势交互+入场动画+出场动画
入场动画
出场手势交互+出场动画
入场动画+出场手势交互+出场动画

UIViewControllerAnimatedTransitioning 定义了转场动画的细节:包括时间和具体的转场动画
UIPercentDrivenInteractiveTransition 定义了转场动画的手势进度,是否取消,是否完成等
UIViewControllerTransitioningDelegate 只是定义了我该如何选择转场动画,如何选择手势交互

也就是说:UIViewControllerTransitioningDelegate 是管理UIPercentDrivenInteractiveTransition 和 UIViewControllerAnimatedTransitioning 的

联系如下:

UIViewControllerTransitioningDelegate
	func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return X<UIViewControllerAnimatedTransitioning>
    }
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return Y<UIViewControllerAnimatedTransitioning>
    }

    func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return Z<UIViewControllerInteractiveTransitioning>
    }
    func interactionControllerForPresentation(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        //ToDo:present interaction
        return W<UIViewControllerInteractiveTransitioning>
    }

    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        //ToDo:.....
        return nil
    }

到目前为止:你已经了解了30%了。
注意:UIViewControllerTransitioningDelegate --》present、dismiss

这个适用于模态转场。
那么导航转场动画呢?

UINavigationControllerDelegate ----》 push、pop

 //动画操作
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
    	if  operation == .push {
    		return X<UIViewControllerAnimatedTransitioning>
    	}else{
	    	return Y<UIViewControllerAnimatedTransitioning>
    	}
    }
    
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return Z<UIViewControllerInteractiveTransitioning>
        
    }
}
再来看:UIViewControllerAnimatedTransitioning
	func transitionDuration(using transitionContext: 	UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.6
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        switch type {
        case .pop:
            doPopAnimation(transitionContext: transitionContext)
        case .push:
            doPushAnimation(transitionContext: transitionContext)
        }
    }

简单的转场动画

扫描二维码关注公众号,回复: 3799322 查看本文章
func doPushAnimation(transitionContext:UIViewControllerContextTransitioning) {
        let screenBounds = UIScreen.main.bounds
        
        
        //from vc view
        guard let from = transitionContext.viewController(forKey: .from)else{
            return
        }
        from.view.frame = CGRect.init(x: 0, y: 0, width: screenBounds.width, height: screenBounds.height)
        
        
        //to vc view
        guard let to = transitionContext.viewController(forKey: .to) else {
            return
        }
        to.view.frame = CGRect.init(x: 0, y: 0, width: screenBounds.width, height: screenBounds.height)
        to.view.alpha = 0
        /**转场动画UIView容器
         */
        let containView = transitionContext.containerView
        
        containView.addSubview(from.view)
        containView.addSubview(to.view)
        
        let duration: TimeInterval = self.transitionDuration(using: transitionContext)
        
        UIView.animate(withDuration: duration, animations: {
            
            to.view.alpha = 1
            
        }) { (_) in
            
            let wasCancelled: Bool = transitionContext.transitionWasCancelled
            transitionContext.completeTransition(!wasCancelled)
        }
    }
    func doPopAnimation(transitionContext:UIViewControllerContextTransitioning) {
        
        
        let screenBounds = UIScreen.main.bounds
        
        
        //from vc view
        guard let from = transitionContext.viewController(forKey: .from)else{
            return
        }
        from.view.frame = CGRect.init(x: 0, y: 0, width: screenBounds.width, height: screenBounds.height)
        
        
        //to vc view
        guard let to = transitionContext.viewController(forKey: .to) else {
            return
        }
        to.view.frame = CGRect.init(x: 0, y: 0, width: screenBounds.width, height: screenBounds.height)
        to.view.alpha = 0
        
        /**转场动画UIView容器
         */
        let containView = transitionContext.containerView
        
        containView.addSubview(from.view)
        containView.addSubview(to.view)
        
        let duration: TimeInterval = self.transitionDuration(using: transitionContext)
        
        UIView.animate(withDuration: duration, animations: {
            
            to.view.alpha = 1
            
        }) { (_) in
            
            let wasCancelled: Bool = transitionContext.transitionWasCancelled
            transitionContext.completeTransition(!wasCancelled)
        }
    }

转场动画,基本完成,下面我们加入手势:
不太好描述,我们来看这个文件:

import UIKit

class QInteractiveTranslationManager: UIPercentDrivenInteractiveTransition {
    
    weak var vc:UIViewController?
    var gestureDirection:QGestureDirection = .down
    private(set) var interation:Bool = false
    private(set) var transitionType:QTranslateType = .dismiss
    
    init(gestureDirection:QGestureDirection) {
        super.init()
        self.gestureDirection = gestureDirection
    }
    
    @objc func handleGesture(gesture:UIPanGestureRecognizer){
        
        var present:CGFloat = 0
        switch gestureDirection {
        case .left:
            let transitionX = -gesture.translation(in: gesture.view).x
            present = transitionX/(gesture.view?.frame.size.width ?? 0)
        case .right:
            let transitionX = gesture.translation(in: gesture.view).x
            present = transitionX/(gesture.view?.frame.size.width ?? 0)
        case .up:
            let transitionY = -gesture.translation(in: gesture.view).y
            present = transitionY/(gesture.view?.frame.size.height ?? 0)
        case .down:
            let transitionY = gesture.translation(in: gesture.view).y
            present = transitionY/(gesture.view?.frame.size.height ?? 0)
        }
        
        switch gesture.state {
        case .began:
            interation = true
            startGesture()
        case .changed:
            //更新百分比
            update(present)
        case .ended:
            interation = false
            if present > 0.3{
                self.finish()
            }else{
                self.cancel()
            }
        default:
            break
        }
    }
    
    func addPanGestureForViewController(_ viewController:UIViewController ){
        let pan = UIPanGestureRecognizer.init(target: self, action: #selector(handleGesture(gesture:)))
        self.vc = viewController
        self.vc?.view.addGestureRecognizer(pan)
    }
    
    
    func startGesture(){
        switch transitionType {
        case .dismiss:
            vc?.dismiss(animated: true, completion: nil)
            
        case .pop:
            vc?.navigationController?.popViewController(animated: true)
        default:
            break
        }
    }
}

为了较少耦合,我决定用一个

class QTransitionController: NSObject,UIViewControllerTransitioningDelegate,UINavigationControllerDelegate

来控制转场动画,但是由于大部分转动画具有高度的不可重用性,所以,想要实现一个结构较好的方式,比较困难。

QTransitionController 可以用于实现总控制,
但是具体的手势交互需要根据需求,QInteractiveTranslationManager 实现了这一点
QPresentTransitionAnimatorLibrary 有present、push的入场动画,
QDismissTransitionAnimatorLibrary 有dismiss、pop的出场动画.
你可以下载来看,demo中并不齐全,但大体思路是这样的,希望有人能补充。

猜你喜欢

转载自blog.csdn.net/Xoxo_x/article/details/83119917