38

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

 5 years ago
source link: https://www.tuicool.com/articles/BJzaMzM
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

自定义转场这个技术相信大家都用过或者听说过,不过如果你经常使用自定义转场或者自定义转场动画做得比较复杂或许会发现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协议方法
  • 在自定义类中定义代理暴露动画控制权给目标控制器,并在适当时机调用代理方法

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


Recommend

  • 59
    • 微信 mp.weixin.qq.com 6 years ago
    • Cache

    [译] 更可靠的 React 组件:合理的封装

  • 85

    源码地址,如果对你有帮助的话希望不要吝啬你的 Star 本文主要记录一下如何基于 Vue 开发组件,并在 npm 上发布。废话不多说,进入正题 Vue 开发插件 开发之前先看看官网的 开发规范 我们开发的之后期望的结果是支持 import、require 或者

  • 44
    • www.cocoachina.com 6 years ago
    • Cache

    iOS | 对封装自定义弹窗的一点思考

    iu 背景 由于项目原因,经常需要封装自定义弹窗。 最开始我封装自定义弹窗的思路是在[UIApplication sharedApplic...

  • 43
    • www.cocoachina.com 6 years ago
    • Cache

    iOS 自定义转场动画

    自定义转场动画集锦.gif 本文记录分享下自定义转场动画的实现方法,具体到动画效果:新浪...

  • 45

    这两天还是在捣鼓collectionView,每当我切换自己自定义的各种奇奇怪怪的collectionViewLayout的时候,我都对苹果对布局切换的动画处理佩服得五体投地,如此丝滑般流畅,同时苹果也将这种丝滑的动画效果用到了自定义转场中,从iOS7开始,在collectionViewControlle...

  • 49
    • 掘金 juejin.im 6 years ago
    • Cache

    封装Vue组件的一些技巧

    本文同步在个人博客shymean.com上,欢迎关注 写Vue有很长一段时间了,除了常规的业务开发之外,也应该思考和反思一下封装组件的正确方式。以弹窗组件为例,一种实现是在需要模板中引入需要弹窗展示的组件,然后通过一个flag变量来控制弹窗的组件,在业务代码

  • 28
    • 掘金 juejin.im 5 years ago
    • Cache

    组件化页面:封装el-table

    项目做的越来越多,重复的东西不断的封装成了组件,慢慢的,页面就组件化了,只需要定义组件配置数据,使用就好了,这是一件非常舒服的事情,这篇文章主要和大家讲讲如何对element-ui中的el-table进行二次封装。 分析需求 公有组件,可以被任何页面调用,首先

  • 5

    目录 1,起因 哪天,正在蚂蚁森林疯狂偷能量的我被boss叫过去,告知我司要做一个线上直播公开课功能的微信小程序,博主第一次写小程序,复习了下文档,看了看腾讯云直播sdk,开工了。 写着写着就发现不对劲...

  • 9

    ​​想了解更多内容,请访问:​​​

  • 8

    作者:凌小凤 ​ 鸿蒙已经提供了全局UI方法自定义弹窗,本文是基于基础的自定义弹窗来实现提示消息弹窗、确认弹窗、输入弹窗的UI组件封装。 一、消息确认弹窗 首先看下效果:

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK