iOS-自定义转场效果

  • Swift5.3
  • 效果图


    效果图.gif

控制器从A——>B
在两个控制器里设置navigation的代理

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        navigationController?.delegate = self
    }

在A控制器里实现下面的方法

//MARK: Navigation Delegate
extension A控制器:UINavigationControllerDelegate,UIViewControllerAnimatedTransitioning{
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 1.0
    }
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
        return self
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {

        //先拿到前后两个viewcontroller 以及 实现动画的容器
        let toVC = transitionContext.viewController(forKey: .to)! as! B控制器
        let containerView = transitionContext.containerView

        //对Cell上的 imageView 截图,同时将这个 imageView 本身隐藏
        let cell = collectionView.cellForItem(at: selectIndexPath!) as! A控制器的cell
        let snapshotView = UIImageView(image: cell.imageView!.image)
        snapshotView.contentMode = .scaleAspectFit

        // 坐标转换 将cell.iconImgV的rect从其父view中转换到containerView视图中,返回在containerView视图中的rect
        snapshotView.frame = containerView.convert((cell.imageView.frame), from: cell.imageView.superview)
        cell.imageView.isHidden = true

        //设置第二个 viewController ,将它的放到过渡后的位置,但让他完全透明,我们会在过渡时给它一个淡入的效果。
        toVC.view.frame = transitionContext.finalFrame(for: toVC)
        toVC.view.alpha = 0
        toVC.imageView.isHidden = true

        //把动画前后的两个ViewController加到容器中,顺序很重要,snapShotView在上方
        containerView.addSubview(toVC.view)
        containerView.addSubview(snapshotView)

        UIView.animate(withDuration: self.transitionDuration(using: transitionContext), delay: 0.0, usingSpringWithDamping: 0.6, initialSpringVelocity: 1.0, options: .curveLinear, animations: {

            //将截图放到第二个viewController的imageView上
            snapshotView.frame = containerView.convert(toVC.imageView.frame, from: toVC.imageView.superview)
            toVC.view.alpha = 1.0

        }) { (finished) in

            snapshotView.removeFromSuperview()
            cell.imageView.isHidden = false
            toVC.imageView.isHidden = false

            //告诉系统动画结束
            transitionContext.completeTransition(true)
        }
    }

}

控制器从B——>A
在B控制器里实现下面的方法

//MARK: Navigation Delegate
extension B控制器:UINavigationControllerDelegate,UIViewControllerAnimatedTransitioning{
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.25
    }
    
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{
        return self
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            
            let vc = transitionContext.viewController(forKey: .to)
            //实现过渡的控制器 和 容器view
            let toVC = vc as! A控制器
            let fromVC = transitionContext.viewController(forKey: .from) as! B控制器的cell
            let containerView = transitionContext.containerView

            //前一个VC设置 截图
            let snapShotView = UIImageView(image: fromVC.imageView.image)
            snapShotView.contentMode = .scaleAspectFit
            snapShotView.frame = containerView.convert(fromVC.imageView.frame, from: fromVC.view)
            fromVC.imageView.isHidden = true
            
            //下一个VC设置
            toVC.view.frame = transitionContext.finalFrame(for: toVC)
            let cell = toVC.collectionView.cellForItem(at: selectIndexPath!) as! HomeCollectionViewCell
            cell.imageView.isHidden = true
            
            // 添加到容器
            containerView.insertSubview(toVC.view, belowSubview: fromVC.view)
            containerView.addSubview(snapShotView)
            
            UIView.animate(withDuration: self.transitionDuration(using: transitionContext), animations: {
                containerView.layoutIfNeeded()
                fromVC.view.alpha = 0
                snapShotView.frame = containerView.convert((cell.imageView.frame), from: cell.imageView.superview)
                
            }) { (finished) in
                
                // 告诉系统动画结束
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
                if transitionContext.transitionWasCancelled {
                    
                    //手势取消了,原来隐藏的imageView要显示出来
                    fromVC.imageView.isHidden = false
                }else {
                    
                    //手势成功,cell的imageView也要显示出来
                    cell.imageView.isHidden = false
                }
                
                //动画交互动作完成或取消后,移除临时动画文件
                snapShotView.removeFromSuperview()
            }
        }

自定义右划返回上一页面的手势动画,在B控制器里实现下面的方法

    var transitionInteractive: HomeTransitionInteractive!

    override func viewDidLoad() {
        super.viewDidLoad()

        let interactive = HomeTransitionInteractive()
        interactive.addPanGestureForViewController(viewController: self)
        transitionInteractive = interactive
    }

    //MARK: Navigation Delegate
extension HomeCellDetailsViewController:UINavigationControllerDelegate,UIViewControllerAnimatedTransitioning{

    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        
        //手势开始的时候才需要传入手势过渡代理,如果直接pop或push,应该返回nil,否者无法正常完成pop/push动作
        return self.transitionInteractive.isInteractive == true ? self.transitionInteractive : nil
    }
}

自定义手势动画HomeTransitionInteractive

//
//  HomeTransitionInteractive.swift
//  PhotoCalendar
//
//  Created by Shi Jiachen on 2021/02/18.
//

import UIKit

class HomeTransitionInteractive: UIPercentDrivenInteractiveTransition {

    var viewController: UIViewController!
    var isInteractive: Bool!
    
    func addPanGestureForViewController(viewController : UIViewController) {
        
        let pan = UIPanGestureRecognizer(target: self, action: #selector(self.handleGesture(panGesture: )))
        self.viewController = viewController
        viewController.view.addGestureRecognizer(pan)
    }
    
    @objc func handleGesture(panGesture:UIPanGestureRecognizer) {
        
        let translation = panGesture.translation(in: panGesture.view)
        var percentComplete:CGFloat = 0.0
        
        //左右滑动的百分比
        percentComplete = translation.x / SCREEN_WIDTH
        percentComplete = abs(percentComplete)
//        print(percentComplete)
        
        switch panGesture.state {
        case .began:
            
            self.isInteractive = true
            self.viewController.navigationController?.popViewController(animated: true)
        
        case .changed:
            
            //手势过程中,通过updateInteractiveTransition设置转场过程动画进行的百分比,然后系统会根据百分比自动布局动画控件,不用我们控制了
            self.update(percentComplete)
            
        case .ended:
            self.isInteractive = false
            //手势完成后结束标记并且判断移动距离是否过半,过则finishInteractiveTransition完成转场操作,否者取消转场操作,转场失败
            if percentComplete > 0.35 {
                self.finish()
            }else {
                self.cancel()
            }
            
        default:
            break
        }
        
    }
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容