iOS动画:ViewController过渡动画之Presentation Controller和Orientation动画(11)

22 篇文章 0 订阅
21 篇文章 0 订阅

Presentation Controller

我们来改变系统自带的present动画,主要用到UIViewControllerTransitioningDelegate的代理方法animationController(forPresented:presenting:source:),该代理方法需要返回一个UIViewControllerAnimatedTransitioning的可选对象,当返回nil时,UIKit将使用内置系统默认动画,当不为nil时,将使用你自己自定义的动画效果。

现在有这样一个场景:
image_1
我们在点击底下image时,需要将图片scale到一个新的界面,而不是采用系统默认的present动画从屏幕底部开始覆盖。

present转场动画

1.我们创建一个动画构造器,命名为PopAnimator.swift

class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
    }
}

2.在FirstViewController里面实现UIViewControllerTransitioningDelegate的代理方法

class ViewController: UIViewController {
	let transition = PopAnimator()
	...
}
extension ViewController: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return transition
    }
    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return nil
    }
}

3.设置SecondViewController的过渡代理

    secondVC.transitioningDelegate = self

上面做了一些基本的初始化,现在我们来实现我们的动画构造器PopAnimator.swift。
首先为PopAnimator添加三个属性:

    let duration = 1.0//动画时间
    var presenting = true//用来判断是present还是dismiss
    var originFrame = CGRect.zero//用来存储image的原始位置

替换transitionDuration()的返回值为

	return duration

现在我们来为animateTransition添加动画,它有一个UIViewControllerContextTransitioning类型的参数。在这之前,你需要了解动画上下文。
当present转场动画开始时,firstView将被添加到转场容器(container)里面,secondView将被创建但是屏幕上不可见,因此我们主要做的就是为容器add一个secondView并赋予进入的动画,为firstView添加移除的动画。默认情况下,firstView并不需要我们remove,动画完成后会自动从container容器中移除。

我们来创建一个简单的淡入动画来测试一下。
添加以下代码到animateTransition()

	let containerView = transitionContext.containerView//获得动画的容器
    let toView = transitionContext.view(forKey: .to)!//取得secondView
    //let toViewController = transitionContext.viewController(forKey: .to)!//取secondViewController
    
	containerView.addSubview(toView)//添加secondView到容器里面
	toView.alpha = 0.0//设置secondView初始透明度为0
	UIView.animate(withDuration: duration, animations: {
		toView.alpha = 1.0
	}) { (_) in
		transitionContext.completeTransition(true)//告诉UIKit转场动画已经完成,转场完成时,firstView会自动从container容器中移除
	}

这样就实现了淡入的转场动画

image_2

pop转场动画

替换animateTransition()里面的代码:

   	let containerView = transitionContext.containerView
    let toView = transitionContext.view(forKey: .to)!
    let secondView = presenting ? toView : transitionContext.view(forKey: .from)!

当present时,secondView是toView,当pop时,secondView变成了fromView。
设置secondView动画前坐标及放大倍数

        let initialFrame = presenting ? originFrame : secondView.frame
        let finalFrame = presenting ? secondView.frame : originFrame
        
        let xScaleFactor = presenting ? initialFrame.width / finalFrame.width : finalFrame.width / initialFrame.width
        
        let yScaleFactor = presenting ? initialFrame.height / finalFrame.height : finalFrame.height / initialFrame.height
        
        let scaleTransform = CGAffineTransform(scaleX: xScaleFactor, y: yScaleFactor)
        if presenting {
            secondView.transform = scaleTransform
            secondView.center = CGPoint(x: initialFrame.midX, y: initialFrame.midY)
            secondView.clipsToBounds = true
        }

添加动画

        containerView.addSubview(toView)
        containerView.bringSubview(toFront: secondView)
        
        UIView.animate(withDuration: duration, delay: 0.0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.0, animations: {
            secondView.transform = self.presenting ? CGAffineTransform.identity : scaleTransform
            secondView.center = CGPoint(x: finalFrame.midX, y: finalFrame.midY)
            secondView.layer.cornerRadius = self.presenting ? 0.0 : 20.0/xScaleFactor
        }) { (_) in
            transitionContext.completeTransition(true)
        }

在FirstViewController中的animationController(forPresented:)方法里面设置PopAnimator的初始值,present动画就做好了。

        transition.originFrame = selectedImage!.superview!.convert(selectedImage!.frame, to: nil)
        transition.presenting = true
        selectedImage!.isHidden = true

在animationController(forDismissed:)里面设置PopAnimator初始值,dismiss动画就做好了。

	transition.presenting = false
	return transition

在dismiss动画完成后,需要将selectImage取消隐藏
为PopAnimator.swift添加一个block

	var dismissCompletion: (()->Void)?

在completeTransition()之前调用

	if !self.presenting {
		self.dismissCompletion?()
	}

在FirstViewController的viewDidLoad里面添加回调显示selectImage

    transition.dismissCompletion = {
        self.selectedImage!.isHidden = false
    }

Orientation动画

设备方向改变时,我们在FirstViewController里重新设置原Image的位置及尺寸

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition: { (context) in
            self.bgImage.alpha = (size.width > size.height) ? 0.25 : 0.55
            self.positionListItems()//重新布局
        }, completion: nil)
    }

运行效果
image_3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值