封装自己的自定义转场组件

栏目: IOS · 发布时间: 4年前

内容简介:自定义转场这个技术相信大家都用过或者听说过,不过如果你经常使用自定义转场或者自定义转场动画做得比较复杂或许会发现AnimatedTransitioning与目标控制器的交互并没有那么友好。本文旨在提供一个新思路,减少AnimatedTransitioning与目标控制器的耦合,引入封装好的类文件后,只需少量代码,就可以实现一个基础的转场动画。首先,我们来看一下实现一个普通的转场动画的流程。将转场方式设置为自定义

自定义转场这个技术相信大家都用过或者听说过,不过如果你经常使用自定义转场或者自定义转场动画做得比较复杂或许会发现AnimatedTransitioning与目标控制器的交互并没有那么友好。本文旨在提供一个新思路,减少AnimatedTransitioning与目标控制器的耦合,引入封装好的类文件后,只需少量代码,就可以实现一个基础的转场动画。

首先,我们来看一下实现一个普通的转场动画的流程。

设置转场方式

将转场方式设置为自定义

toVC.modalPresentationStyle = .custom

设置转场代理

这里转场代理可以设置为目标控制器,也可以是我们自定义的管理转场的类,这里设置为目标控制器

toVC.transitioningDelegate = toVC

实现转场代理方法

转场的代理方法我们需要实现以下两个方法,指定我们自定义的类来管理转场动画,关于这个自定义的类,后面会详细说。

public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
	//这里的animationTransitionContr就是自定义的用来管理转场的类
	animationTransitionContr.isPresenting = true
       return animationTransitionContr
   }
   
   public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
   	//这里的animationTransitionContr就是自定义的用来管理转场的类
	animationTransitionContr.isPresenting = false
       return animationTransitionContr
   }

自定义转场动画管理类

这个管理类定为NSObject子类就可以,关键是它必须遵从 UIViewControllerAnimatedTransitioning 协议,并实现以下几个代理方法。

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.25
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView
        guard let fromVC = transitionContext.viewController(forKey: .from) else { return }
        guard let toVC = transitionContext.viewController(forKey: .to) else { return }
        if isPresenting {
            // 进入目标控制器的动画
        } else {
            // 退出目标控制器的动画
        }
        
    }

至此,一个常规的自定义转场动画就完成了。到这里就会发现一个问题,在实现具体动画的这一步,只有单个控制器使用这个转场类还好,如果是多个控制器分别需要实现不同的动画效果,这里的逻辑就会很复杂,代码可读性也不好。

要解决这个问题,我们可以把重构一下动画逻辑,将动画代码放在转场类里面,而将动画的具体实现放在目标控制器里。具体实现如下:

为转场控制类定义代理

这里的代理方法是将动画的不同时间点的控制暴露给目标控制器

	protocol ZYAnimationTransitionControllerDelegate: NSObjectProtocol {
    func willPresent(fromView: UIView, toView: UIView)
    func onPresent(fromView: UIView, toView: UIView)
    func didPresent(fromView: UIView, toView: UIView)
    func willDismiss(fromView: UIView, toView: UIView)
    func onDismiss(fromView: UIView, toView: UIView)
    func didDismiss(fromView: UIView, toView: UIView)
}

在适当的时机调用代理方法

fileprivate func present(transitionContext: UIViewControllerContextTransitioning, container: UIView, fromView: UIView, toView: UIView, completion: @escaping () -> Void) {
    container.addSubview(toView)
    guard let delegate = delegate else { return }
    delegate.willPresent(fromView: fromView, toView: toView)
    self.startAnimation(transitionContext: transitionContext, animations: {
        delegate.onPresent(fromView: fromView, toView: toView)
    }) {
        delegate.didPresent(fromView: fromView, toView: toView)
        completion()
    }
}

fileprivate func dismiss(transitionContext: UIViewControllerContextTransitioning, container: UIView, fromView: UIView, toView: UIView, completion: @escaping () -> Void) {
    container.addSubview(fromView)
    guard let delegate = delegate else { return }
    delegate.willDismiss(fromView: fromView, toView: toView)
    self .startAnimation(transitionContext: transitionContext, animations: {
        delegate.onDismiss(fromView: fromView, toView: toView)
    }) {
        delegate.didDismiss(fromView: fromView, toView: toView)
        completion()
    }
}

这里使用了一个辅助方法:

fileprivate func startAnimation(transitionContext: UIViewControllerContextTransitioning, animations: @escaping () -> Void, completion: @escaping () -> Void) {
    UIApplication.shared.beginIgnoringInteractionEvents()
    UIView.animate(withDuration: self.transitionDuration(using: self as? UIViewControllerContextTransitioning), delay: 0, options: UIView.AnimationOptions(rawValue: 7 << 16), animations: animations, completion: { _ in
        UIApplication.shared.endIgnoringInteractionEvents()
        completion()
    })
}

重构转场代理方法

用以下方法替换之前的代理方法实现:

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView
        guard let fromVC = transitionContext.viewController(forKey: .from) else { return }
        guard let toVC = transitionContext.viewController(forKey: .to) else { return }
        if isPresenting {
            present(transitionContext: transitionContext, container: containerView, fromView: fromVC.view, toView: toVC.view) {
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
        } else {
            dismiss(transitionContext: transitionContext, container: containerView, fromView: fromVC.view, toView: toVC.view) {
                transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
            }
        }
    }

至此,动画逻辑的重构就完成了。这时,一个完整的自定义转场的实现逻辑就是这样的:

  • 目标控制器设置转场类型为自定义
  • 目标控制器设置转场代理为自己
  • 目标控制器在转场代理方法中返回自定义的转场控制类
  • 在自定义转场控制类中实现UIViewControllerAnimatedTransitioning协议方法
  • 在自定义类中定义代理暴露动画控制权给目标控制器,并在适当时机调用代理方法

相关代码的完整实现可以参考我的图片预览框架 源码


以上所述就是小编给大家介绍的《封装自己的自定义转场组件》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

离心力:互联网历史与数字化未来

离心力:互联网历史与数字化未来

[英] 乔尼·赖安(Johnny Ryan) / 段铁铮 / 译言·东西文库/电子工业出版社 / 2018-2-1 / 68.00元

★一部详实、严谨的互联网史著作; ★哈佛、斯坦福等高校学生必读书目; ★《互联网的未来》作者乔纳森·L. 齐特雷恩,《独立报》《爱尔兰时报》等知名作者和国外媒体联合推荐。 【内容简介】 虽然互联网从诞生至今,不过是五六十年,但我们已然有必要整理其丰富的历史。未来的数字世界不仅取决于我 们的设想,也取决于它的发展历程,以及互联网伟大先驱们的理想和信念。 本书作者乔尼· ......一起来看看 《离心力:互联网历史与数字化未来》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器