// // LargeImageShrinkAnimationController.swift // Tusker // // Created by Shadowfacts on 9/1/18. // Copyright © 2018 Shadowfacts. All rights reserved. // import UIKit import Gifu class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTransitioning { let interactionController: LargeImageInteractionController? init(interactionController: LargeImageInteractionController?) { self.interactionController = interactionController } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return 0.2 } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageAnimatableViewController, let toVC = transitionContext.viewController(forKey: .to) else { return } if UIAccessibility.prefersCrossFadeTransitions && !transitionContext.isInteractive { animateCrossFadeTransition(using: transitionContext) return } guard let sourceView = fromVC.animationSourceView, let sourceFrame = fromVC.sourceViewFrame(in: toVC.view), let image = fromVC.animationImage else { transitionContext.completeTransition(!transitionContext.transitionWasCancelled) return } // use alpha, becaus isHidden makes stack views re-layout sourceView.alpha = 0 let containerView = transitionContext.containerView let originalVCFrame = fromVC.view.frame var originalFrameSize = originalVCFrame.inset(by: fromVC.view.safeAreaInsets).size let newWidth = originalFrameSize.width / image.size.width let newHeight = originalFrameSize.height / image.size.height if newHeight < newWidth { originalFrameSize.width = newHeight * image.size.width } else { originalFrameSize.height = newWidth * image.size.height } let originalFrame = CGRect(origin: CGPoint(x: originalVCFrame.midX - originalFrameSize.width / 2, y: originalVCFrame.midY - originalFrameSize.height / 2), size: originalFrameSize) let imageView = GIFImageView(frame: originalFrame) imageView.image = image if let gifData = fromVC.animationGifData { imageView.animate(withGIFData: gifData) } imageView.contentMode = .scaleAspectFill imageView.layer.cornerRadius = 0 imageView.layer.masksToBounds = true let blackView = UIView(frame: originalVCFrame) blackView.backgroundColor = .black blackView.alpha = 1 containerView.addSubview(toVC.view) containerView.addSubview(blackView) containerView.addSubview(imageView) let duration = transitionDuration(using: transitionContext) UIView.animate(withDuration: duration, animations: { imageView.frame = sourceFrame imageView.layer.cornerRadius = sourceView.layer.cornerRadius imageView.layer.maskedCorners = sourceView.layer.maskedCorners blackView.alpha = 0 }, completion: { _ in blackView.removeFromSuperview() imageView.removeFromSuperview() if transitionContext.transitionWasCancelled { toVC.view.removeFromSuperview() } sourceView.alpha = 1 transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) } func animateCrossFadeTransition(using transitionContext: UIViewControllerContextTransitioning) { guard let fromVC = transitionContext.viewController(forKey: .from) as? LargeImageAnimatableViewController, let toVC = transitionContext.viewController(forKey: .to) else { return } transitionContext.containerView.addSubview(toVC.view) transitionContext.containerView.addSubview(fromVC.view) let duration = transitionDuration(using: transitionContext) UIView.animate(withDuration: duration) { fromVC.view.alpha = 0 } completion: { (_) in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) } } }