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

Usage

  1. Implement the ZoomTransitionSource protocol to the source view controller.
  2. Implement the ZoomTransitionDestination protocol to the destination view controller.
  3. Initialise a ZoomTransitionAnimator instance and return it when asked in UINavigationControllerDelegate.

@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