自定义过渡动画

来源

https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/CustomizingtheTransitionAnimations.html#//apple_ref/doc/uid/TP40007457-CH16-SW1

过渡动画是把两个controller的内容交换。有两种交换类型:

  1. presentations:加一个新的view controller到你app的 view controller层级。
  2. dismissals:从app里移除一个或多个view controller。

the transitioning delegate

transitioning delegate是你自定义呈现和过渡动画的起点。the transitioning delegate是一个你定义的并遵守UIViewControllerTransitioningDelegate协议的对象。它提供给UIKit以下对象:

  1. animator objects:它是创建动画,并用来展示或隐藏一个view controller的view.这个transitioning delegate可以分别提供animator object 给presenting 和dismissing. animator object 遵守UIViewControllerAnimatedTransitioning协议。
  2. interactive animator objects:这个对象可以用事件或手势和驱动自定义动画的时间。它遵守UIViewControllerInteractiveTransitioning协议。
    最简单的创建一个interaction animator 是继承UIPercentDrivenInteractiveTransition 类,并加事件处理代码到你的子类里。这个类控制了你的动画时间(这个动画是用你已存在的animator objects创建的)。如果你要自己创建你自己的interaction animator,你必须自己来渲染动画里的每一帧(还是不要这样做--PPF)。
  3. presentation controller: 当一个view controller展示在屏幕上时, presentation controller管理着展示方式。系统为内置的展示类型提供了presentation controller,并且你也可以为您自己的展示类型提供自定义的presentation controller(这部分在下一篇会讲--PPF)。

给 view controller的transitioningDelegate属性赋值一个transitioning delegate,用于告诉UIKit 你要执行一个自定义的过渡和展示方式。你的delegate可以选择提供哪个对象。如果你不提供animator objects,UIKit会使用标准的过渡动画,在view controller的modalTransitionStyle的属性里。

只有当view controller的modalpresentationStype的属性被设为UIModelPresentationCustom时,自定义的presentation controller才会被使用。


自定义过渡动画_第1张图片

自定义动画的调用顺序。

当被呈现的view controller的transitioningDelegate属性包含一个有效值时,UIKit就会用你提供的动画对象(animator objects)来呈现那个view controller.当它已经准备好呈现时,UIKit就会调用transition delegate里的 animationControllerForPresentedController:presentingController:sourceController:方法来接收一个动画对象(animator object).如果对象有效,UIKit就会执行以下步骤:

  1. UIKit 会调用transitioning delegate的interactionControllerForPresentation:方法去看一下是否有一个interactive animator(交互动画对象)有效。如果是返回nil,UIKit就会执行一个没有用户交互的动画。
  2. UIKit会调用animator object的 transitionDuration:得到动画持续时间。
  3. UIKit会调用以下一个合适的方法开始动画:
  • 没有interactive animations,UIKit会调用 animator 对象的 animateTransition:。
  • 有interactive animations,UIKit会调用interactive animator对象的 startInteractiveTransition:
  1. UIKit会等着,等着animator对象的context transitioning对象调用 completeTransition: 。
    你自己的自定义的animator会在动画结束后调用这个方法,一般会在动画结束块里(completion block)。调用这个方法来结束过渡,并且让UIKit知道这可以调用presentViewController:animated:completion: 的completion handler(结束块)和调用animator 对象自己的animationEnded:方法。

当dismissing一个view controller时,UIKit会调用你的transitioning delegate的animationControllerForDismissedController:并执行以下步骤:

  1. UIKit调用transition delegate的interactionControllerForDismissal:方法去看一下是否有一个有效的interactive animator(交互动画对象)。如果是返回nil,UIKit就会执行一个没有用户交互的动画。
  2. UIKit会调用animator object的 transitionDuration:得到动画持续时间。
  3. UIKit会调用以下一个合适的方法开始动画:
    • 没有interactive animations,UIKit会调用 animator 对象的 animateTransition:。
  • 有interactive animations,UIKit会调用interactive animator对象的 startInteractiveTransition:
  1. UIKit会等着,等着animator对象的context transitioning对象调用 completeTransition:
    你自己的自定义的animator会在动画结束后调用这个方法,一般会在动画结束块里(completion block)。调用这个方法来结束过渡,并且让UIKit知道这可以调用presentViewController:animated:completion:的completion handler(结束块)和调用animator 对象自己的animationEnded:方法。
注意:

必须在你的动画结束时调用completeTransition:。UIKit不会结束过渡过程,并因此把控制权还给你的app,直到你调用了这个方法。

The Transitioning Context Object

注意:

当你设置自定义的动画时,一定要只使用在transitioning context对象里的对象和数据,而不是你自己管理的缓存数据。过渡是会发生多种多样的情况下,而有一些情况可能会改变动画参数。the transitioning context对象保证会有你需要的正确信息来执行动画,然而你自己的缓存的信息有可能在动画方法被调用时就失效了(不新鲜了)

Presenting a View Controller Using Custom Animations

用自定义的动画要呈现一个新的view controller, 在已存在的view controller的活动方法里加以下步聚:

  1. 创建一个你要呈现的view controller.
  2. 创建你自己的transitioning delegate对象,并把它赋给新view controller的transitioningDelegate属性。这个transitioning delegate的方法会在需要的时候提供你自定义的animator对象。
  3. 调用presentViewController:animated:completion:方法,呈现新的view controller。

试一下

如果已经有一个segue了,以上的步骤是否可以在prepareForSegue:里进行。

Getting the Animation Parameters

  • 调用viewControllerForKey:分别得到“from"和”to“两个view controller.
  • 调用containerView方法得到动画的父视图。加入所有的关键subView到这里。比如:加入presented view controller的view。
  • 调用viewForKey:方法得到要加入或移除的view.
  • 调用finalFrameForViewController:方法得到被加入或移除的view的最终frame。

  • from:总是在过渡开始时在屏幕上的view controller.
  • to:总是在过渡结束时在屏幕上的view controller.

动画的主要目标:

  • presentation:加入”to“view 到container view。
  • dismissal: 从container view里移除”from“view。

Creating the Transition Animations

  • presentation animations:

  • 使用viewControllerForKey:viewForKey:方法得到view controllers 和views。

  • 设置 "to"view的开始位置。还有其它属性的起始值(比如transform)。

  • finalFrameForViewController:方法得到"to"view的结束位置。

  • 把"to"view作为一个subview加入container view.

  • 创建动画:

  • 在你的动画块里,在container view里把"to"view移动到它的最终位置。设置最终值到它的其它属性里。

  • 在动画结束块里(completion block)里,调用completeTransition:方法,执行其它清理工作。

  • Dismissal animations:

  • 使用viewControllerForKey:viewForKey:方法得到view controllers 和views。

  • 计算"from"view的结束位置。这个presented view controller里的view 现在是要被dismissed。

  • 把"to"view作为一个subview加入container view.
    在过渡结束时,presenting view controller的view("from" view)会被移除。在dismissal过程中,你必须把它加回到container view。

  • 创建动画:

  • 在你的动画块里,在container view里把"from"view移动到它的最终位置。设置最终值到它的其它属性里。

  • 在动画结束块里(completion block)里,移除"from" view,调用completeTransition:方法,执行其它清理工作。

import UIKit

class PresentationAnimator: NSObject,UIViewControllerAnimatedTransitioning {
    
    // true:presenting   false:dismissable
    let presenting:Bool
    
    init(presenting:Bool) {
        self.presenting = presenting
        super.init()
    }
    
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 1.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        //获取关联的对象
        let containerView = transitionContext.containerView
        let fromVC  = transitionContext.viewController(forKey: .from)!
        let toVC    = transitionContext.viewController(forKey: .to)!
        
        let toV     = transitionContext.view(forKey: .to)!
        let fromV   = transitionContext.view(forKey: .from)!
        
        //设置一些动画的变量。
        let containerFrame = containerView.frame
        
        var toVStartFrame = transitionContext.initialFrame(for: toVC)
        let toVFinalFrame = transitionContext.finalFrame(for: toVC)
        
        var fromVFinalFrame = transitionContext.finalFrame(for: fromVC)
        
        if self.presenting {
            toVStartFrame.origin.x = containerFrame.width
            toVStartFrame.origin.y = containerFrame.height

            containerView.addSubview(toV)
            toV.frame = toVStartFrame
        }else{
            fromVFinalFrame.origin.x = containerFrame.width
            fromVFinalFrame.origin.y = containerFrame.height
            fromVFinalFrame.size.width = toV.frame.size.width
            fromVFinalFrame.size.height = toV.frame.size.height

            containerView.insertSubview(toV, at: 0)
            toV.frame = toVFinalFrame
        }
        
        
        UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { 
            if self.presenting{
                toV.frame = toVFinalFrame
            }else{
                fromV.frame = fromVFinalFrame
            }
        }) { (_) in
            let success = !transitionContext.transitionWasCancelled
            // After a failed presentation or successful dismissal, remove the view.
            if (self.presenting && !success) || (!self.presenting && success){
                toV.removeFromSuperview()
            }
            transitionContext.completeTransition(success)
        }
    }
}

中间还有一段interface animator 等以后再看看........


以上的东西也可以用在navigation controller上。push pop.

self.navigationController?.delegate = self
let percentDriven =  UIPercentDrivenInteractiveTransition()
extension ViewController:UINavigationControllerDelegate{
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        switch operation {
        case .push:
            //animator object
            return PresentationAnimator(presenting: true)
        case .pop:
            return PresentationAnimator(presenting: false)
        default:
            return nil
        }
    }
}

    func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        return percentDriven;
    }

你可能感兴趣的:(自定义过渡动画)