自定義控制器轉(zhuǎn)場(chǎng)動(dòng)畫(huà)

前言

系統(tǒng)自帶的push和pop動(dòng)畫(huà)已經(jīng)能滿足絕大部份使用場(chǎng)景了。騰訊新聞、汽車之家等App的push和pop動(dòng)畫(huà)和系統(tǒng)的不一樣,是自定義的,找了點(diǎn)空閑時(shí)間,研究了哈自定義控制器轉(zhuǎn)場(chǎng)動(dòng)畫(huà)。

QQ20170515-153720.gif

1、聲明一個(gè)枚舉,自定義一個(gè)指定構(gòu)造器方法,根據(jù)枚舉值來(lái)確定該使用push或者pop動(dòng)畫(huà)

    enum TransitionAniamtionsType {
    case push,pop
}
    var transitionAniamtionsType: TransitionAniamtionsType = .push
    
    public init(type: TransitionAniamtionsType) {
        transitionAniamtionsType = type
    }

2、新建一個(gè)類,遵守<UIViewControllerAnimatedTransitioning>協(xié)議,實(shí)現(xiàn)兩個(gè)必需實(shí)現(xiàn)的方法

    //返回轉(zhuǎn)場(chǎng)動(dòng)畫(huà)持續(xù)時(shí)間
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }
    //轉(zhuǎn)場(chǎng)動(dòng)畫(huà)的具體實(shí)現(xiàn)
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        if transitionAniamtionsType == .push {
            pushAnimation(using: transitionContext)
        }else if transitionAniamtionsType == .pop {
            popAnimation(using: transitionContext)
        }
    }

3、具體動(dòng)畫(huà)邏輯,本人實(shí)現(xiàn)的動(dòng)畫(huà)比較簡(jiǎn)單,喜歡研究的大神可以根據(jù)需要實(shí)現(xiàn)更復(fù)雜的動(dòng)畫(huà)。具體動(dòng)畫(huà)實(shí)現(xiàn)代碼如下

    //push
    fileprivate func pushAnimation(using transitionContext: UIViewControllerContextTransitioning) {
        //獲取fromVc和toVc的view
        guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return }
        let containerView = transitionContext.containerView
        //設(shè)置toVc的view的frame,fromVc就是當(dāng)前界面上的vc就不需要設(shè)置和添加了
        toView.frame = CGRect(x: screen.width, y: 64, width: screen.width, height: screen.height - 64)
        containerView.addSubview(toView)
        //執(zhí)行動(dòng)畫(huà)
        UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
            fromView.alpha = 0.8
            fromView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
            toView.frame = CGRect(x: 0, y: 64, width: self.screen.width, height: self.screen.height - 64)
        }) { (finish) in
            //!transitionContext.transitionWasCancelled 轉(zhuǎn)場(chǎng)動(dòng)畫(huà)是否被取消(手勢(shì)交互需要這樣寫,如果不需要手勢(shì)的話直接寫成true就可以了)
            //transitionContext.completeTransition這個(gè)方法在動(dòng)畫(huà)執(zhí)行完后必需調(diào)用
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
    //pop
    fileprivate func popAnimation(using transitionContext: UIViewControllerContextTransitioning) {
        
        guard let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from),
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to) else { return }
        let containerView = transitionContext.containerView
        
        toView.transform = CGAffineTransform.init(scaleX: 0.9, y: 0.9)
        toView.alpha = 0.8
        containerView.insertSubview(toView, at: 0)
        
        UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
            toView.alpha = 1
            toView.transform = CGAffineTransform.identity
            fromView.frame = CGRect(x: self.screen.width, y: 64, width: self.screen.width, height: self.screen.height - 64)
        }) { (finish) in
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
        
    }

4、在需要?jiǎng)赢?huà)的控制中設(shè)置代理,實(shí)現(xiàn)代理方法

    \\每次顯示這個(gè)控制器的view的時(shí)候都重新設(shè)置代理,不這樣的做的話pop回來(lái),下次在push的時(shí)候就不會(huì)去執(zhí)行返回動(dòng)畫(huà)的代理了
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        self.navigationController?.delegate = self
    }

    extension ViewController: UINavigationControllerDelegate {
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == .push {
            return TransitionAnimations.init(type: .push)
        }else if operation == .pop {
            return TransitionAnimations.init(type: .pop)
        }else {
            return nil
        }
    }
}

5、手勢(shì)交互

手勢(shì)交互實(shí)現(xiàn)起來(lái)還是比較方便的,蘋果已經(jīng)為我們封裝好了相關(guān)的方法了,我們只需要添加一個(gè)手勢(shì)來(lái)更新相關(guān)參數(shù)就可以了
    //在viewDidLoad中實(shí)現(xiàn)
    //聲明一個(gè)交互對(duì)象
    var interactiveTransition: UIPercentDrivenInteractiveTransition?

    func initializeInterface() {
        self.navigationController?.delegate = self
        view.backgroundColor = UIColor.yellow
        let panGesture: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(respondsToPanGesture))
        view.addGestureRecognizer(panGesture)
    }
    
    fileprivate func respondsToPanGesture(pan: UIPanGestureRecognizer) {
        let moveX = pan.translation(in: pan.view).x
        let percent = moveX / screen.width
        switch pan.state {
        case .began:
            interactiveTransition = UIPercentDrivenInteractiveTransition()
            navigationController?.popViewController(animated: true)
        case .changed:
            //根據(jù)百分比更新交互
            interactiveTransition?.update(percent)
        case .ended:
            if percent > 0.5 {
                interactiveTransition?.finish()
            }else {
                interactiveTransition?.cancel()
            }
            interactiveTransition = nil
        default:
            print("default")
        }
        
    }

最后在該控制器中實(shí)現(xiàn)navigationdelegate的方法

extension BViewController: UINavigationControllerDelegate {
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        if operation == .push {
            return TransitionAnimations.init(type: .push)
        }else if operation == .pop {
            return TransitionAnimations.init(type: .pop)
        }else {
            return nil
        }
    }
    
    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        // 返回交互對(duì)象
        return interactiveTransition
    }
    
}

Demo 地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容