约定:
底层控制器:在层级较低的控制器,由它创建新控制器
转场控制器:就是底层控制器创建的新控制器
转场控制器被创建出来,若要自定义转场的动画,比如从中心逐渐放大知道占据满屏,这样系统没有提供的动画,就需要底层控制器来创建新的转场动画。
首先需要底层控制器创建新控制器的时候,给新控制器约定管理转场与动画的代理。如:
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.的循环引用的问题】
}