Improve gallery expand animation
Use spring timing, slide in top/bottom controls
This commit is contained in:
parent
f5110c773a
commit
e19a6528ad
|
@ -27,6 +27,10 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
|
||||||
}
|
}
|
||||||
|
|
||||||
var animationSourceView: UIImageView? { sourceViews[currentIndex] }
|
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? {
|
var animationImage: UIImage? {
|
||||||
if let page = pages[currentIndex] as? LargeImageAnimatableViewController,
|
if let page = pages[currentIndex] as? LargeImageAnimatableViewController,
|
||||||
let image = page.animationImage {
|
let image = page.animationImage {
|
||||||
|
@ -48,6 +52,9 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
|
||||||
override var prefersStatusBarHidden: Bool {
|
override var prefersStatusBarHidden: Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
override var childForHomeIndicatorAutoHidden: UIViewController? {
|
override var childForHomeIndicatorAutoHidden: UIViewController? {
|
||||||
return viewControllers?.first
|
return viewControllers?.first
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
typealias ContentView = UIView & LargeImageContentView
|
typealias ContentView = UIView & LargeImageContentView
|
||||||
|
|
||||||
weak var animationSourceView: UIImageView?
|
weak var animationSourceView: UIImageView?
|
||||||
|
var largeImageController: LargeImageViewController? { self }
|
||||||
var animationImage: UIImage? { contentView.animationImage }
|
var animationImage: UIImage? { contentView.animationImage }
|
||||||
var animationGifData: Data? { contentView.animationGifData }
|
var animationGifData: Data? { contentView.animationGifData }
|
||||||
var dismissInteractionController: LargeImageInteractionController?
|
var dismissInteractionController: LargeImageInteractionController?
|
||||||
|
@ -49,6 +50,9 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate, LargeIma
|
||||||
override var prefersStatusBarHidden: Bool {
|
override var prefersStatusBarHidden: Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
override var prefersHomeIndicatorAutoHidden: Bool {
|
override var prefersHomeIndicatorAutoHidden: Bool {
|
||||||
return !controlsVisible
|
return !controlsVisible
|
||||||
|
|
|
@ -36,6 +36,7 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
|
||||||
var shrinkGestureEnabled = true
|
var shrinkGestureEnabled = true
|
||||||
|
|
||||||
weak var animationSourceView: UIImageView?
|
weak var animationSourceView: UIImageView?
|
||||||
|
var largeImageController: LargeImageViewController? { largeImageVC }
|
||||||
var animationImage: UIImage? { largeImageVC?.animationImage ?? animationSourceView?.image }
|
var animationImage: UIImage? { largeImageVC?.animationImage ?? animationSourceView?.image }
|
||||||
var animationGifData: Data? { largeImageVC?.animationGifData }
|
var animationGifData: Data? { largeImageVC?.animationGifData }
|
||||||
var dismissInteractionController: LargeImageInteractionController?
|
var dismissInteractionController: LargeImageInteractionController?
|
||||||
|
@ -43,6 +44,9 @@ class LoadingLargeImageViewController: UIViewController, LargeImageAnimatableVie
|
||||||
override var prefersStatusBarHidden: Bool {
|
override var prefersStatusBarHidden: Bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
|
||||||
|
return .none
|
||||||
|
}
|
||||||
override var childForHomeIndicatorAutoHidden: UIViewController? {
|
override var childForHomeIndicatorAutoHidden: UIViewController? {
|
||||||
return largeImageVC
|
return largeImageVC
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import Gifu
|
||||||
|
|
||||||
protocol LargeImageAnimatableViewController: UIViewController {
|
protocol LargeImageAnimatableViewController: UIViewController {
|
||||||
var animationSourceView: UIImageView? { get }
|
var animationSourceView: UIImageView? { get }
|
||||||
|
var largeImageController: LargeImageViewController? { get }
|
||||||
var animationImage: UIImage? { get }
|
var animationImage: UIImage? { get }
|
||||||
var animationGifData: Data? { get }
|
var animationGifData: Data? { get }
|
||||||
var dismissInteractionController: LargeImageInteractionController? { get }
|
var dismissInteractionController: LargeImageInteractionController? { get }
|
||||||
|
@ -37,7 +38,7 @@ extension LargeImageAnimatableViewController {
|
||||||
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTransitioning {
|
||||||
|
|
||||||
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
|
||||||
return 0.2
|
return 0.5
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
|
||||||
|
@ -47,19 +48,22 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
}
|
}
|
||||||
|
|
||||||
let containerView = transitionContext.containerView
|
let containerView = transitionContext.containerView
|
||||||
containerView.addSubview(toVC.view)
|
|
||||||
|
|
||||||
let finalVCFrame = transitionContext.finalFrame(for: toVC)
|
let finalVCFrame = transitionContext.finalFrame(for: toVC)
|
||||||
guard let sourceView = toVC.animationSourceView,
|
guard let sourceView = toVC.animationSourceView,
|
||||||
let sourceFrame = toVC.sourceViewFrame(in: fromVC.view),
|
let sourceFrame = toVC.sourceViewFrame(in: fromVC.view),
|
||||||
let image = toVC.animationImage else {
|
let image = toVC.animationImage else {
|
||||||
toVC.view.frame = finalVCFrame
|
toVC.view.frame = finalVCFrame
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// use alpha, becaus isHidden makes stack views re-layout
|
// use alpha, because isHidden makes stack views re-layout
|
||||||
sourceView.alpha = 0
|
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
|
var finalFrameSize = finalVCFrame.inset(by: fromVC.view.safeAreaInsets).size
|
||||||
let newWidth = finalFrameSize.width / image.size.width
|
let newWidth = finalFrameSize.width / image.size.width
|
||||||
|
@ -81,21 +85,17 @@ class LargeImageExpandAnimationController: NSObject, UIViewControllerAnimatedTra
|
||||||
imageView.layer.maskedCorners = sourceView.layer.maskedCorners
|
imageView.layer.maskedCorners = sourceView.layer.maskedCorners
|
||||||
imageView.layer.masksToBounds = true
|
imageView.layer.masksToBounds = true
|
||||||
|
|
||||||
let blackView = UIView(frame: finalVCFrame)
|
containerView.addSubview(toVC.view)
|
||||||
blackView.backgroundColor = .black
|
|
||||||
blackView.alpha = 0
|
|
||||||
|
|
||||||
containerView.addSubview(blackView)
|
|
||||||
containerView.addSubview(imageView)
|
containerView.addSubview(imageView)
|
||||||
|
|
||||||
toVC.view.isHidden = true
|
|
||||||
|
|
||||||
let duration = transitionDuration(using: transitionContext)
|
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.frame = finalFrame
|
||||||
imageView.layer.cornerRadius = 0
|
imageView.layer.cornerRadius = 0
|
||||||
blackView.alpha = 1
|
toVC.view.alpha = 1
|
||||||
}, completion: { _ in
|
toVC.largeImageController?.setControlsVisible(true, animated: false)
|
||||||
|
} completion: { (_) in
|
||||||
// This shouldn't be necessary. I believe it's a workaround for using a XIB
|
// 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
|
// 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
|
// 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.
|
// (or UIKit does layout differently when loading the view) and this is not necessary.
|
||||||
toVC.view.frame = finalVCFrame
|
toVC.view.frame = finalVCFrame
|
||||||
|
|
||||||
toVC.view.isHidden = false
|
toVC.largeImageController?.contentView.isHidden = false
|
||||||
fromVC.view.isHidden = false
|
fromVC.view.isHidden = false
|
||||||
blackView.removeFromSuperview()
|
|
||||||
imageView.removeFromSuperview()
|
imageView.removeFromSuperview()
|
||||||
|
|
||||||
sourceView.alpha = 1
|
sourceView.alpha = 1
|
||||||
|
|
||||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue