Last active
December 6, 2017 20:36
-
-
Save yesleon/03da25df9661b1164a21b2c20328b2f7 to your computer and use it in GitHub Desktop.
A transition animator class which zooms into a specified UIView.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ZoomTransitionAnimator.swift | |
// ScrapBeta | |
// | |
// Created by Li-Heng Hsu on 06/12/2017. | |
// Copyright © 2017 Li-Heng Hsu. All rights reserved. | |
// | |
import UIKit | |
protocol ZoomTransitionSource { | |
func zoomTransitionAnimator(_ animator: ZoomTransitionAnimator, targetViewBeginningFrameFor operation: ZoomTransitionOperation) -> CGRect | |
} | |
protocol ZoomTransitionDestination { | |
func zoomTransitionAnimator(_ animator: ZoomTransitionAnimator, targetViewFinalFrameFor operation: ZoomTransitionOperation) -> CGRect | |
} | |
enum ZoomTransitionOperation: Int { | |
case zoomIn = 1, zoomOut = 2 | |
} | |
class ZoomTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning { | |
let duration: TimeInterval | |
let damping: CGFloat | |
let operation: ZoomTransitionOperation | |
let source: ZoomTransitionSource | |
let destination: ZoomTransitionDestination | |
var targetViewBeginningFrame: CGRect { | |
return source.zoomTransitionAnimator(self, targetViewBeginningFrameFor: operation) | |
} | |
var targetViewFinalFrame: CGRect { | |
return destination.zoomTransitionAnimator(self, targetViewFinalFrameFor: operation) | |
} | |
init(duration: TimeInterval = 0.4, damping: CGFloat = 0.8, source: ZoomTransitionSource, destination: ZoomTransitionDestination, operation: ZoomTransitionOperation) { | |
self.duration = duration | |
self.damping = damping | |
self.source = source | |
self.destination = destination | |
self.operation = operation | |
} | |
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | |
return duration | |
} | |
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | |
guard let fromView = transitionContext.view(forKey: .from) else { fatalError() } | |
guard let toView = transitionContext.view(forKey: .to) else { fatalError() } | |
let containerView = transitionContext.containerView | |
let zoomInRatio = targetViewFinalFrame.width / targetViewBeginningFrame.width | |
let toViewFirstFrame: CGRect = { | |
let firstX = targetViewBeginningFrame.origin.x - (targetViewFinalFrame.origin.x / zoomInRatio) | |
let firstY = targetViewBeginningFrame.origin.y - (targetViewFinalFrame.origin.y / zoomInRatio) | |
let width = toView.frame.width / zoomInRatio | |
let height = toView.frame.height / zoomInRatio | |
return CGRect(x: firstX, y: firstY, width: width, height: height) | |
}() | |
let fromViewFinalFrame: CGRect = { | |
let finalX = (fromView.frame.origin.x - targetViewBeginningFrame.origin.x) * zoomInRatio + targetViewFinalFrame.origin.x | |
let finalY = (fromView.frame.origin.y - targetViewBeginningFrame.origin.y) * zoomInRatio + targetViewFinalFrame.origin.y | |
let width = fromView.frame.width * zoomInRatio | |
let height = fromView.frame.height * zoomInRatio | |
return CGRect(x: finalX, y: finalY, width: width, height: height) | |
}() | |
let fromViewSnapshotView = fromView.snapshotView(afterScreenUpdates: false)! | |
containerView.addSubview(fromViewSnapshotView) | |
containerView.insertSubview(toView, at: 0) | |
let backgroundView = UIView(frame: containerView.bounds) | |
backgroundView.backgroundColor = fromView.backgroundColor | |
containerView.insertSubview(backgroundView, aboveSubview: toView) | |
let toViewSnapshotView = toView.snapshotView(afterScreenUpdates: true)! | |
toViewSnapshotView.alpha = 0 | |
toViewSnapshotView.frame = toViewFirstFrame | |
containerView.addSubview(toViewSnapshotView) | |
fromView.isHidden = true | |
containerView.backgroundColor = toView.backgroundColor | |
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: damping, initialSpringVelocity: 0, options: .curveLinear, animations: { | |
fromViewSnapshotView.frame = fromViewFinalFrame | |
toViewSnapshotView.frame = toView.frame | |
toViewSnapshotView.alpha = 1 | |
}, completion: { (completed) in | |
fromView.isHidden = false | |
containerView.backgroundColor = nil | |
fromViewSnapshotView.removeFromSuperview() | |
toViewSnapshotView.removeFromSuperview() | |
backgroundView.removeFromSuperview() | |
transitionContext.completeTransition(!transitionContext.transitionWasCancelled) | |
}) | |
} | |
} |
Updated
- One class for both zoom-in and zoom-out actions.
- Modified the protocols so a view controller could be both a zoom-out source and a zoom-in source.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
ZoomTransitionSource
protocol to the source view controller.ZoomTransitionDestination
protocol to the destination view controller.ZoomTransitionAnimator
instance and return it when asked inUINavigationControllerDelegate
.