Skip to content

Instantly share code, notes, and snippets.

@yesleon
Last active December 6, 2017 20:36
Show Gist options
  • Save yesleon/03da25df9661b1164a21b2c20328b2f7 to your computer and use it in GitHub Desktop.
Save yesleon/03da25df9661b1164a21b2c20328b2f7 to your computer and use it in GitHub Desktop.
A transition animator class which zooms into a specified UIView.
//
// 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)
})
}
}
@yesleon
Copy link
Author

yesleon commented Dec 6, 2017

Updated

  1. One class for both zoom-in and zoom-out actions.
  2. 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