自定义过渡动画

来源

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才会被使用。


自定义动画的调用顺序。

当被呈现的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;
    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容