Improve gallery expand animation

Use spring timing, slide in top/bottom controls
This commit is contained in:
Shadowfacts 2020-09-08 23:41:11 -04:00
parent f5110c773a
commit e19a6528ad
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
4 changed files with 32 additions and 18 deletions

View File

@ -27,6 +27,10 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
}
var animationSourceView: UIImageView? { sourceViews[currentIndex] }
var largeImageController: LargeImageViewController? {
// use protocol because page controllers may be loading or non-loading VCs
(pages[currentIndex] as? LargeImageAnimatableViewController)?.largeImageController
}
var animationImage: UIImage? {
if let page = pages[currentIndex] as? LargeImageAnimatableViewController,
let image = page.animationImage {
@ -48,6 +52,9 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
override var prefersStatusBarHidden: Bool {
return true
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .none
}
override var childForHomeIndicatorAutoHidden: UIViewController? {
return viewControllers?.first
}

View File

@ -13,6 +13,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
typealias ContentView = UIView & LargeImageContentView
weak var animationSourceView: UIImageView?
var largeImageController: LargeImageViewController? { self }
var animationImage: UIImage? { contentView.animationImage }
var animationGifData: Data? { contentView.animationGifData }
var dismissInteractionController: LargeImageInteractionController?
@ -49,6 +50,9 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
override var prefersStatusBarHidden: Bool {
return true
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .none
}
override var prefersHomeIndicatorAutoHidden: Bool {
return !controlsVisible

View File

@ -36,6 +36,7 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
var shrinkGestureEnabled = true
weak var animationSourceView: UIImageView?
var largeImageController: LargeImageViewController? { largeImageVC }
var animationImage: UIImage? { largeImageVC?.animationImage ?? animationSourceView?.image }
var animationGifData: Data? { largeImageVC?.animationGifData }
var dismissInteractionController: LargeImageInteractionController?
@ -43,6 +44,9 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
override var prefersStatusBarHidden: Bool {
return true
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .none
}
override var childForHomeIndicatorAutoHidden: UIViewController? {
return largeImageVC
}

View File

@ -11,6 +11,7 @@ import Gifu
protocol LargeImageAnimatableViewController: UIViewController {
var animationSourceView: UIImageView? { get }
var largeImageController: LargeImageViewController? { get }
var animationImage: UIImage? { get }
var animationGifData: Data? { get }
var dismissInteractionController: LargeImageInteractionController? { get }
@ -37,7 +38,7 @@ extension LargeImageAnimatableViewController {
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.2
return 0.5
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
@ -47,19 +48,22 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
}
let containerView = transitionContext.containerView
containerView.addSubview(toVC.view)
let finalVCFrame = transitionContext.finalFrame(for: toVC)
guard let sourceView = toVC.animationSourceView,
let sourceFrame = toVC.sourceViewFrame(in: fromVC.view),
let image = toVC.animationImage else {
let sourceFrame = toVC.sourceViewFrame(in: fromVC.view),
let image = toVC.animationImage else {
toVC.view.frame = finalVCFrame
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
return
}
// use alpha, becaus isHidden makes stack views re-layout
// use alpha, because isHidden makes stack views re-layout
sourceView.alpha = 0
toVC.view.alpha = 0
toVC.largeImageController?.contentView.isHidden = true
toVC.largeImageController?.setControlsVisible(false, animated: false)
var finalFrameSize = finalVCFrame.inset(by: fromVC.view.safeAreaInsets).size
let newWidth = finalFrameSize.width / image.size.width
@ -81,21 +85,17 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
imageView.layer.maskedCorners = sourceView.layer.maskedCorners
imageView.layer.masksToBounds = true
let blackView = UIView(frame: finalVCFrame)
blackView.backgroundColor = .black
blackView.alpha = 0
containerView.addSubview(blackView)
containerView.addSubview(toVC.view)
containerView.addSubview(imageView)
toVC.view.isHidden = true
let duration = transitionDuration(using: transitionContext)
UIView.animate(withDuration: duration, animations: {
let velocity = 1 / CGFloat(duration)
UIView.animate(withDuration: duration, delay: 0, usingSpringWithDamping: 0.65, initialSpringVelocity: velocity, options: []) {
imageView.frame = finalFrame
imageView.layer.cornerRadius = 0
blackView.alpha = 1
}, completion: { _ in
toVC.view.alpha = 1
toVC.largeImageController?.setControlsVisible(true, animated: false)
} completion: { (_) in
// This shouldn't be necessary. I believe it's a workaround for using a XIB
// for the large image VC. Without this, the final frame of the large image VC
// is not set to the propper rect (it uses the frame of the preview device
@ -103,15 +103,14 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
// (or UIKit does layout differently when loading the view) and this is not necessary.
toVC.view.frame = finalVCFrame
toVC.view.isHidden = false
toVC.largeImageController?.contentView.isHidden = false
fromVC.view.isHidden = false
blackView.removeFromSuperview()
imageView.removeFromSuperview()
sourceView.alpha = 1
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
}
}