学习一下push和present的自定义转场动画。
demo地址:https://github.com/weiman152/TransitionDemo
从iOS7开始,苹果更新了自定义ViewController转场的API。
几个protocol:
讲自定义转场就离不开这几个protocol:
UIViewControllerContextTransitioning
UIViewControllerAnimatedTransitioning
UIViewControllerInteractiveTransitioning
UIViewControllerTransitioningDelegate
UINavigationControllerDelegate
UITabBarControllerDelegate
我们这个demo中就使用了
UIViewControllerContextTransitioning,
UIViewControllerAnimatedTransitioning,
UIViewControllerTransitioningDelegate,
UINavigationControllerDelegate。
实现效果:
push:
present:
源码:
PushTransition
//
// PushTransition.swift
// TransitionDemo
//
// Created by iOS on 2018/10/26.
// Copyright © 2018年 weiman. All rights reserved.
//
import UIKit
class PushTransition: NSObject {
enum `Type` {
case scale
case formTop
case fromBottom
}
private let ScreenWidth = UIScreen.main.bounds.size.width
private let ScreenHeight = UIScreen.main.bounds.size.height
static private var duration = 0.5
static private var type: Type = .scale
// 设置转场代理
static var transition = PushTransition()
/// 带动画的push
///
/// - Parameters:
/// - fromVC: 发起push操作的VC
/// - toVC: 即将被push出来的VC
/// - type: 需要哪种动画
/// - duration: 动画执行的时间
static func pushWithTransition(fromVC: UIViewController,
toVC: UIViewController,
type: Type = .scale,
duration: Double = 0.5) {
self.type = type
self.duration = duration
fromVC.navigationController?.delegate = PushTransition.transition
fromVC.navigationController?.pushViewController(toVC, animated: true)
fromVC.navigationController?.delegate = nil
}
}
extension PushTransition: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return PushTransition.duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let toVC = transitionContext.viewController(forKey: .to)
let fromVC = transitionContext.viewController(forKey: .from)
guard let fromView = fromVC?.view,
let toView = toVC?.view else {
return
}
switch PushTransition.type {
case .scale:
scale(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
case .formTop:
fromTop(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
case .fromBottom:
fromBottom(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
}
}
}
extension PushTransition: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PushTransition()
}
}
extension PushTransition {
/// 缩放的push
///
/// - Parameters:
/// - fromView: 发起push操作的VC的View
/// - toView: 即将被push出来的VC的view
/// - transitionContext: 上下文
private func scale(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
transitionContext.containerView.addSubview(toView)
transitionContext.containerView.bringSubviewToFront(fromView)
UIView.animate(withDuration: PushTransition.duration, animations: {
fromView.alpha = 0
fromView.transform = CGAffineTransform(scaleX: 0.2, y: 0.2)
toView.alpha = 1.0
}) { (_) in
fromView.transform = CGAffineTransform(scaleX: 1, y: 1)
transitionContext.completeTransition(true)
fromView.alpha = 1.0
}
}
/// 从上到下的push
///
/// - Parameters:
/// - fromView: 发起push操作的VC的View
/// - toView: 即将被push出来的VC的view
/// - transitionContext: 上下文
private func fromTop(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
transitionContext.containerView.addSubview(toView)
toView.transform = CGAffineTransform(translationX: 0,
y: -self.ScreenHeight)
UIView.animate(withDuration: PushTransition.duration, animations: {
toView.transform = CGAffineTransform(translationX: 0, y: 0)
toView.alpha = 1.0
}) { (_) in
transitionContext.completeTransition(true)
}
}
/// 从下往上的push
///
/// - Parameters:
/// - fromView: 发起push操作的VC的View
/// - toView: 即将被push出来的VC的view
/// - transitionContext: 上下文
private func fromBottom(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
transitionContext.containerView.addSubview(toView)
toView.transform = CGAffineTransform(translationX: 0,
y: self.ScreenHeight)
UIView.animate(withDuration: PushTransition.duration, animations: {
toView.transform = CGAffineTransform(translationX: 0, y: 0)
toView.alpha = 1.0
}) { (_) in
transitionContext.completeTransition(true)
}
}
}
PresentTransition:
//
// PresentTransition.swift
// TransitionDemo
//
// Created by iOS on 2018/10/26.
// Copyright © 2018年 weiman. All rights reserved.
//
import UIKit
class PresentTransition: NSObject {
enum Animate {
case scale
case fromLeft
case fromRight
}
enum Transition {
case present
case dismiss
}
private let ScreenWidth = UIScreen.main.bounds.size.width
private let ScreenHeight = UIScreen.main.bounds.size.height
// 设置转场代理
static private let transition = PresentTransition()
static private var duration = 1.0
static private var type: Animate = .scale
static private var tran: Transition = .present
static func presentWithAnimate(fromVC: UIViewController,
toVC: UIViewController,
duration: Double = 1.0,
animate: Animate = .scale) {
PresentTransition.duration = duration
PresentTransition.type = animate
toVC.transitioningDelegate = PresentTransition.transition
fromVC.present(toVC, animated: true) { }
}
}
extension PresentTransition: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return PresentTransition.duration
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
switch PresentTransition.tran {
case .present:
presentTransition(transitionContext: transitionContext)
case .dismiss:
dismissTransition(transitionContext: transitionContext)
}
}
}
extension PresentTransition: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
PresentTransition.tran = .present
return PresentTransition()
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
PresentTransition.tran = .dismiss
return PresentTransition()
}
}
extension PresentTransition {
private func presentTransition(transitionContext: UIViewControllerContextTransitioning) {
let toVC = transitionContext.viewController(forKey: .to)
let fromVC = transitionContext.viewController(forKey: .from)
guard let fromView = fromVC?.view,
let toView = toVC?.view else {
return
}
transitionContext.containerView.addSubview(toView)
switch PresentTransition.type {
case .scale:
scalePresent(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
case .fromLeft:
fromLeftPresent(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
case .fromRight:
fromRightPresent(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
}
}
private func dismissTransition(transitionContext: UIViewControllerContextTransitioning) {
let toVC = transitionContext.viewController(forKey: .to)
let fromVC = transitionContext.viewController(forKey: .from)
guard let fromView = fromVC?.view,
let toView = toVC?.view else {
return
}
switch PresentTransition.type {
case .scale:
scaleDismiss(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
case .fromLeft:
fromLeftDismiss(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
case .fromRight:
fromRightDismiss(fromView: fromView,
toView: toView,
transitionContext: transitionContext)
}
}
private func scalePresent(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
toView.alpha = 0.0
toView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1)
UIView.animate(withDuration: PresentTransition.duration, animations: {
toView.alpha = 1.0
toView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
}) { (_) in
toView.alpha = 1.0
toView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
transitionContext.completeTransition(true)
}
}
private func scaleDismiss(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
transitionContext.containerView.insertSubview(toView, belowSubview: fromView)
UIView.animate(withDuration: PresentTransition.duration, animations: {
fromView.transform = CGAffineTransform(scaleX: 0.01, y: 0.01)
}) { (_) in
transitionContext.completeTransition(true)
}
}
private func fromLeftPresent(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
toView.frame = CGRect(x: -ScreenWidth, y: 0, width: ScreenWidth, height: ScreenHeight)
UIView.animate(withDuration: PresentTransition.duration, animations: {
toView.frame = CGRect(x: 0, y: 0, width: self.ScreenWidth, height: self.ScreenHeight)
toView.layoutSubviews()
}) { (_) in
transitionContext.completeTransition(true)
}
}
private func fromLeftDismiss(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
transitionContext.containerView.insertSubview(toView, belowSubview: fromView)
UIView.animate(withDuration: PresentTransition.duration, animations: {
fromView.transform = CGAffineTransform(translationX: 0, y: self.ScreenHeight)
fromView.alpha = 0
}) { (_) in
transitionContext.completeTransition(true)
}
}
private func fromRightPresent(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
toView.frame = CGRect(x: ScreenWidth, y: 0, width: ScreenWidth, height: ScreenHeight)
UIView.animate(withDuration: PresentTransition.duration, animations: {
toView.frame = CGRect(x: 0, y: 0, width: self.ScreenWidth, height: self.ScreenHeight)
toView.layoutSubviews()
}) { (_) in
transitionContext.completeTransition(true)
}
}
private func fromRightDismiss(fromView: UIView,
toView: UIView,
transitionContext: UIViewControllerContextTransitioning)
{
transitionContext.containerView.insertSubview(toView, belowSubview: fromView)
UIView.animate(withDuration: PresentTransition.duration, animations: {
fromView.transform = CGAffineTransform(translationX: 0, y: self.ScreenHeight)
fromView.alpha = 0
}) { (_) in
transitionContext.completeTransition(true)
}
}
}
参考文章:
https://www.jianshu.com/p/e7155f938e59