iOS基础 自定义转场控制器上的动画 UIPresentationController

概述

约定:
底层控制器:在层级较低的控制器,由它创建新控制器
转场控制器:就是底层控制器创建的新控制器

转场控制器被创建出来,若要自定义转场的动画,比如从中心逐渐放大知道占据满屏,这样系统没有提供的动画,就需要底层控制器来创建新的转场动画。

首先需要底层控制器创建新控制器的时候,给新控制器约定管理转场与动画的代理。如:

accountVC.transitioningDelegate = self

当然这里也可以不使用当前控制器作为转场动画的代理,而是重新创建一个对象作为转场动画的代理。这个部分放在文末解释。

然后底层控制器需要实现相关的协议,即实现控制器转场协议:
UIViewControllerTransitioningDelegate
指定管理转场动画的对象,这里制定管理动画的对象为底层控制器。
如:

extension BasicController:UIViewControllerTransitioningDelegate{
    
    
    //返回控制自定义转场动画 的 控制器
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    
    
        return self
    }
}

然后便是底层控制器实现相关的协议:

extension BasicController : UIViewControllerAnimatedTransitioning{
    
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    
    
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    
    
    }
}

transitionContext是转场动画的上下文,和绘图上下文一个意思。
由于自定义动画转场了,所以原本系统会做的动作,现在都需要在方法里面自己实现。

extension BasicController : UIViewControllerAnimatedTransitioning{
    
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    
    
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    
    
        //获取弹出的View .from 消失的View .to 弹出的View
        let presentView = (transitionContext.view(forKey: .to))!
        //将弹出的View添加到ContainerView中
        transitionContext.containerView.addSubview(presentView)
        
        presentView.transform = CGAffineTransform.init(translationX: -presentView.frame.width + LeftSlideController.LeftSlideMargin, y: 0)
        
        UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
    
    
            //制定自定义的动画
            presentView.transform = CGAffineTransform.identity
        } completion: {
    
     isFinish in
            transitionContext.completeTransition(true)
        }

    }
}

最后记得在动画完成后,要告诉系统转场动画已经完成。
transitionContext.completeTransition(true)

退出的转场动画

同理:

func animateTransitionDismiss(transitionContext: UIViewControllerContextTransitioning){
    
    
        //获取消失的View .from 消失的View .to 弹出的View
        let dismissView = (transitionContext.view(forKey: .from))!
        UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
    
    
            //制定自定义的动画
            dismissView.transform = CGAffineTransform.init(translationX: -500, y: 0)
        } completion: {
    
     isFinish in
            dismissView.removeFromSuperview()
            transitionContext.completeTransition(true)
        }
    }

对转场动画进行封装

由于这个是自定义的转场动画,所有的动画细节都需要程序员我们自己去处理,所以才需要构建一个代理。即底层控制器:

accountVC.transitioningDelegate = ???

所以就需要创建一个工具类来专门处理这个转场动画。
一般情况下构建工具类都是继承自NSObject的,然后再吧先关的代码给移植到工具类中,如下:

class LeftSlideAnimatior: NSObject {
    
    
    var isShowLeftSlideController = false
}

extension LeftSlideAnimatior:UIViewControllerTransitioningDelegate{
    
    
    // 返回控制自定义转场 的 控制器
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
    
    
        return LeftSlideController(presentedViewController: presented, presenting: presenting)
    }
    // 弹出 返回控制自定义转场动画 的 控制器
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    
    
        isShowLeftSlideController = true
        return self
    }
    // 消失 返回控制自定义转场动画 的 控制器
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    
    
        isShowLeftSlideController = false
        return self
    }
}

//
extension LeftSlideAnimatior : UIViewControllerAnimatedTransitioning{
    
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
    
    
        return 0.4
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
    
    
        
        isShowLeftSlideController ? animateTransitionUsing(transitionContext: transitionContext) : animateTransitionDismiss(transitionContext: transitionContext)
        
    }
    
    func animateTransitionUsing(transitionContext: UIViewControllerContextTransitioning){
    
    
        //获取弹出的View .from 消失的View .to 弹出的View
        let presentView = (transitionContext.view(forKey: .to))!
        //将弹出的View添加到ContainerView中
        transitionContext.containerView.addSubview(presentView)
        
        presentView.transform = CGAffineTransform.init(translationX: -presentView.frame.width, y: 0)
        
        UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
    
    
            //制定自定义的动画
            presentView.transform = CGAffineTransform.identity
        } completion: {
    
     isFinish in
            transitionContext.completeTransition(true)
        }
    }
    
    func animateTransitionDismiss(transitionContext: UIViewControllerContextTransitioning){
    
    
        //获取消失的View .from 消失的View .to 弹出的View
        let dismissView = (transitionContext.view(forKey: .from))!
        UIView.animate(withDuration: transitionDuration(using: transitionContext)) {
    
    
            //制定自定义的动画
            dismissView.transform = CGAffineTransform.init(translationX: -dismissView.frame.width, y: 0)
        } completion: {
    
     isFinish in
            dismissView.removeFromSuperview()
            transitionContext.completeTransition(true)
        }
    }
}

而在底层控制器中:

class BasicController: UIViewController {
    
    
    private lazy var leftSildeAnimatior = LeftSlideAnimatior()
}
let accountVC = AccountController()
accountVC.modalPresentationStyle = .custom
accountVC.view.backgroundColor = UIColor.systemOrange
accountVC.transitioningDelegate = leftSildeAnimatior
present(accountVC, animated: true) {
    
    
    //TODO:
}

转场前后的一些个性化设置

因为自定义了转场控制器,所以有了更多的个性化创意的想法,又可能想在转场前改变一些控件的设置,或者想要在转场后更新底层控制器的一些UI或者显示的结果。这就需要在控制器改变状态的时候做一些调整。

这样的解决方案很多,不乏有:

  • 通知
  • 代理
  • 闭包

其中通知不大合适,因为底层控制器和转场控制器之间的层级不是相差太大,两者之间有一定的联系,所以更加适合的是代理和闭包形式。

这里介绍如何使用闭包来解决两级控制器之间的消息传递。

因为我们要在状态改变的时候进行设置,也就是相关的代码应该发生在以下的位置:

// 弹出 返回控制自定义转场动画 的 控制器
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    
    
    isShowLeftSlideController = true
    [此处应该有闭包执行]
    return self
}
// 消失 返回控制自定义转场动画 的 控制器
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    
    
    isShowLeftSlideController = false
    [此处应该有闭包执行]
    return self
}

应该在工具类处定义执行闭包:

class LeftSlideAnimatior: NSObject {
    
    
    var isShowLeftSlideController = false
    var transitionSetting:((_ isShowLeftSlideController:Bool)->())?
    
    init(withTransitionSetting:@escaping ((Bool)->())) {
    
    
        self.transitionSetting = withTransitionSetting
    }
}

在底层控制器创建转场动画代理对象的实例化实现:

class BasicController: UIViewController {
    
    
    
    private lazy var leftSildeAnimatior = LeftSlideAnimatior {
    
    [weak self] isShowLeftSlideVC in
        //此处可以做写转场前后的设置【要注意self.的循环引用的问题】
    }

猜你喜欢

转载自blog.csdn.net/kkkenty/article/details/124946996
今日推荐