From f2e08e96f3347c53528f316e7ebb451b7e109ebe Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 25 Jan 2020 22:29:12 -0500 Subject: [PATCH] Improve large image/gallery animation handling when images aren't loaded --- .../UIViewController+Delegates.swift | 10 +++++--- .../Gallery/GalleryViewController.swift | 8 ++++++ .../GalleryExpandAnimationController.swift | 25 ++++++++++--------- .../GalleryShrinkAnimationController.swift | 7 +++--- .../LargeImageViewController.swift | 6 ++++- .../LargeImageExpandAnimationController.swift | 24 ++++++++++-------- .../LargeImageShrinkAnimationController.swift | 13 +++++----- Tusker/TuskerNavigationDelegate.swift | 4 +-- 8 files changed, 58 insertions(+), 39 deletions(-) diff --git a/Tusker/Extensions/UIViewController+Delegates.swift b/Tusker/Extensions/UIViewController+Delegates.swift index 68728c87..27e87026 100644 --- a/Tusker/Extensions/UIViewController+Delegates.swift +++ b/Tusker/Extensions/UIViewController+Delegates.swift @@ -10,20 +10,22 @@ import UIKit extension UIViewController: UIViewControllerTransitioningDelegate { public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { - if presented is LargeImageViewController { + if let presented = presented as? LargeImageViewController, + presented.sourceInfo?.image != nil { return LargeImageExpandAnimationController() } else if let presented = presented as? GalleryViewController, - presented.sourcesInfo[presented.startIndex] != nil { + presented.sourcesInfo[presented.startIndex]?.image != nil { return GalleryExpandAnimationController() } return nil } public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - if let dismissed = dismissed as? LargeImageViewController { + if let dismissed = dismissed as? LargeImageViewController, + dismissed.imageForDismissalAnimation() != nil { return LargeImageShrinkAnimationController(interactionController: dismissed.dismissInteractionController) } else if let dismissed = dismissed as? GalleryViewController, - dismissed.sourcesInfo[dismissed.currentIndex] != nil { + dismissed.imageForDismissalAnimation() != nil { return GalleryShrinkAnimationController(interactionController: dismissed.dismissInteractionController) } return nil diff --git a/Tusker/Screens/Gallery/GalleryViewController.swift b/Tusker/Screens/Gallery/GalleryViewController.swift index e5429831..da27a092 100644 --- a/Tusker/Screens/Gallery/GalleryViewController.swift +++ b/Tusker/Screens/Gallery/GalleryViewController.swift @@ -96,6 +96,14 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc vc.player?.play() } } + + func imageForDismissalAnimation() -> UIImage? { + if let sourceImage = sourcesInfo[currentIndex]?.image { + return sourceImage + } else { + return (pages[currentIndex] as? AttachmentViewController)?.largeImageVC?.image + } + } // MARK: - Page View Controller Data Source diff --git a/Tusker/Screens/Gallery/Transitions/GalleryExpandAnimationController.swift b/Tusker/Screens/Gallery/Transitions/GalleryExpandAnimationController.swift index 0d5d9e72..03033d9f 100644 --- a/Tusker/Screens/Gallery/Transitions/GalleryExpandAnimationController.swift +++ b/Tusker/Screens/Gallery/Transitions/GalleryExpandAnimationController.swift @@ -20,16 +20,18 @@ class GalleryExpandAnimationController: NSObject, UIViewControllerAnimatedTransi return } - let finalVCFrame = transitionContext.finalFrame(for: toVC) - guard let (image, sourceFrame, sourceCornerRadius) = toVC.sourcesInfo[toVC.startIndex] else { - toVC.view.frame = finalVCFrame - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - return - } - - let attachment = toVC.attachments[toVC.startIndex] - let containerView = transitionContext.containerView + containerView.addSubview(toVC.view) + + let finalVCFrame = transitionContext.finalFrame(for: toVC) + guard let sourceInfo = toVC.sourcesInfo[toVC.startIndex], + let image = sourceInfo.image else { + toVC.view.frame = finalVCFrame + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + return + } + + let attachment = toVC.attachments[toVC.startIndex] let ratio = image.size.width / image.size.height var width = finalVCFrame.width @@ -42,21 +44,20 @@ class GalleryExpandAnimationController: NSObject, UIViewControllerAnimatedTransi } let finalFrame = CGRect(x: finalVCFrame.midX - width / 2, y: finalVCFrame.midY - height / 2, width: width, height: height) - let imageView = GIFImageView(frame: sourceFrame) + let imageView = GIFImageView(frame: sourceInfo.frame) imageView.image = image if attachment.url.pathExtension == "gif", let data = ImageCache.attachments.get(attachment.url) { imageView.animate(withGIFData: data) } imageView.contentMode = .scaleAspectFill - imageView.layer.cornerRadius = sourceCornerRadius + imageView.layer.cornerRadius = sourceInfo.cornerRadius imageView.layer.masksToBounds = true let blackView = UIView(frame: finalVCFrame) blackView.backgroundColor = .black blackView.alpha = 0 - containerView.addSubview(toVC.view) containerView.addSubview(blackView) containerView.addSubview(imageView) diff --git a/Tusker/Screens/Gallery/Transitions/GalleryShrinkAnimationController.swift b/Tusker/Screens/Gallery/Transitions/GalleryShrinkAnimationController.swift index 0bd2022f..d3fdf9c6 100644 --- a/Tusker/Screens/Gallery/Transitions/GalleryShrinkAnimationController.swift +++ b/Tusker/Screens/Gallery/Transitions/GalleryShrinkAnimationController.swift @@ -26,7 +26,8 @@ class GalleryShrinkAnimationController: NSObject, UIViewControllerAnimatedTransi return } - guard let (image, sourceFrame, sourceCornerRadius) = fromVC.sourcesInfo[fromVC.currentIndex] else { + guard let sourceInfo = fromVC.sourcesInfo[fromVC.currentIndex], + let image = fromVC.imageForDismissalAnimation() else { transitionContext.completeTransition(!transitionContext.transitionWasCancelled) return } @@ -66,8 +67,8 @@ class GalleryShrinkAnimationController: NSObject, UIViewControllerAnimatedTransi let duration = transitionDuration(using: transitionContext) UIView.animate(withDuration: duration, animations: { - imageView.frame = sourceFrame - imageView.layer.cornerRadius = sourceCornerRadius + imageView.frame = sourceInfo.frame + imageView.layer.cornerRadius = sourceInfo.cornerRadius blackView.alpha = 0 }, completion: { _ in blackView.removeFromSuperview() diff --git a/Tusker/Screens/Large Image/LargeImageViewController.swift b/Tusker/Screens/Large Image/LargeImageViewController.swift index 45720835..5658b1d5 100644 --- a/Tusker/Screens/Large Image/LargeImageViewController.swift +++ b/Tusker/Screens/Large Image/LargeImageViewController.swift @@ -13,7 +13,7 @@ import Gifu class LargeImageViewController: UIViewController, UIScrollViewDelegate { - typealias SourceInfo = (image: UIImage, frame: CGRect, cornerRadius: CGFloat) + typealias SourceInfo = (image: UIImage?, frame: CGRect, cornerRadius: CGFloat) var sourceInfo: SourceInfo? var dismissInteractionController: LargeImageInteractionController? @@ -132,6 +132,10 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate { } } } + + func imageForDismissalAnimation() -> UIImage? { + return sourceInfo?.image ?? image + } func setControlsVisible(_ controlsVisible: Bool, animated: Bool) { self.controlsVisible = controlsVisible diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift index 086a010f..049ef9c9 100644 --- a/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift +++ b/Tusker/Screens/Large Image/Transitions/LargeImageExpandAnimationController.swift @@ -21,33 +21,35 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra return } - let finalVCFrame = transitionContext.finalFrame(for: toVC) - guard let (image, originFrame, originCornerRadius) = toVC.sourceInfo else { - toVC.view.frame = finalVCFrame - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - return - } - let containerView = transitionContext.containerView + containerView.addSubview(toVC.view) + + let finalVCFrame = transitionContext.finalFrame(for: toVC) + guard let sourceInfo = toVC.sourceInfo, + let image = sourceInfo.image else { + toVC.view.frame = finalVCFrame + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + return + } + let ratio = image.size.width / image.size.height let width = finalVCFrame.width let height = width / ratio let finalFrame = CGRect(x: finalVCFrame.midX - width / 2, y: finalVCFrame.midY - height / 2, width: width, height: height) - let imageView = GIFImageView(frame: originFrame) - imageView.image = toVC.imageView.image! + let imageView = GIFImageView(frame: sourceInfo.frame) + imageView.image = image if let gifData = toVC.gifData { imageView.animate(withGIFData: gifData) } imageView.contentMode = .scaleAspectFill - imageView.layer.cornerRadius = originCornerRadius + imageView.layer.cornerRadius = sourceInfo.cornerRadius imageView.layer.masksToBounds = true let blackView = UIView(frame: finalVCFrame) blackView.backgroundColor = .black blackView.alpha = 0 - containerView.addSubview(toVC.view) containerView.addSubview(blackView) containerView.addSubview(imageView) diff --git a/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift b/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift index ef10f080..1aebef1e 100644 --- a/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift +++ b/Tusker/Screens/Large Image/Transitions/LargeImageShrinkAnimationController.swift @@ -27,11 +27,12 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra return } - guard let (image, finalFrame, finalCornerRadius) = fromVC.sourceInfo else { - transitionContext.completeTransition(!transitionContext.transitionWasCancelled) - return + guard let sourceInfo = fromVC.sourceInfo, + let image = fromVC.imageForDismissalAnimation() else { + transitionContext.completeTransition(!transitionContext.transitionWasCancelled) + return } - + let originalVCFrame = fromVC.view.frame let containerView = transitionContext.containerView @@ -59,8 +60,8 @@ class LargeImageShrinkAnimationController: NSObject, UIViewControllerAnimatedTra let duration = transitionDuration(using: transitionContext) UIView.animate(withDuration: duration, animations: { - imageView.frame = finalFrame - imageView.layer.cornerRadius = finalCornerRadius + imageView.frame = sourceInfo.frame + imageView.layer.cornerRadius = sourceInfo.cornerRadius blackView.alpha = 0 }, completion: { _ in blackView.removeFromSuperview() diff --git a/Tusker/TuskerNavigationDelegate.swift b/Tusker/TuskerNavigationDelegate.swift index 04a0dc02..3c8b5750 100644 --- a/Tusker/TuskerNavigationDelegate.swift +++ b/Tusker/TuskerNavigationDelegate.swift @@ -148,7 +148,7 @@ extension TuskerNavigationDelegate where Self: UIViewController { } private func sourceViewInfo(_ sourceView: UIImageView?) -> LargeImageViewController.SourceInfo? { - guard let sourceView = sourceView, let image = sourceView.image else { return nil } + guard let sourceView = sourceView else { return nil } var sourceFrame = sourceView.convert(sourceView.bounds, to: view) if let scrollView = view as? UIScrollView { @@ -159,7 +159,7 @@ extension TuskerNavigationDelegate where Self: UIViewController { let y = sourceFrame.minY * scale - scrollView.contentOffset.y + scrollView.frame.minY sourceFrame = CGRect(x: x, y: y, width: width, height: height) } - return (image: image, frame: sourceFrame, cornerRadius: sourceView.layer.cornerRadius) + return (image: sourceView.image, frame: sourceFrame, cornerRadius: sourceView.layer.cornerRadius) } func largeImage(_ image: UIImage, description: String?, sourceView: UIImageView) -> LargeImageViewController {