Improve gallery transitions when there is something displaying on top of the source view

See #520
This commit is contained in:
Shadowfacts 2024-11-24 19:17:02 -05:00
parent f44dae632c
commit a99fb7f0b0
3 changed files with 75 additions and 4 deletions

View File

@ -47,6 +47,18 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
container.addSubview(to.view) container.addSubview(to.view)
} }
// This trick does not work as well here as it does for presentation, seemingly
// because the delayFactor:0.9 still results in the snapshot fading out too quickly :/
let sourceSnapshot = sourceView.snapshotView(afterScreenUpdates: false)
if let sourceSnapshot {
let snapshotContainer = sourceView.ancestorForInsertingSnapshot
snapshotContainer.addSubview(sourceSnapshot)
let sourceFrameInShapshotContainer = snapshotContainer.convert(sourceView.bounds, from: sourceView)
sourceSnapshot.frame = sourceFrameInShapshotContainer
sourceSnapshot.layer.opacity = 1
self.sourceView.layer.opacity = 0
}
let sourceFrameInContainer = container.convert(sourceView.bounds, from: sourceView) let sourceFrameInContainer = container.convert(sourceView.bounds, from: sourceView)
let destFrameInContainer = container.convert(itemViewController.content.view.bounds, from: itemViewController.content.view) let destFrameInContainer = container.convert(itemViewController.content.view.bounds, from: itemViewController.content.view)
@ -59,6 +71,7 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
.translatedBy(x: destFrameInContainer.midX - sourceFrameInContainer.midX, y: destFrameInContainer.midY - sourceFrameInContainer.midY) .translatedBy(x: destFrameInContainer.midX - sourceFrameInContainer.midX, y: destFrameInContainer.midY - sourceFrameInContainer.midY)
.scaledBy(x: scale, y: scale) .scaledBy(x: scale, y: scale)
sourceView.transform = sourceToDestTransform sourceView.transform = sourceToDestTransform
sourceSnapshot?.transform = sourceToDestTransform
} else { } else {
appliedSourceToDestTransform = false appliedSourceToDestTransform = false
} }
@ -142,6 +155,7 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
if appliedSourceToDestTransform { if appliedSourceToDestTransform {
self.sourceView.transform = origSourceTransform self.sourceView.transform = origSourceTransform
sourceSnapshot?.transform = origSourceTransform
} }
contentContainer.frame = sourceFrameInContainer contentContainer.frame = sourceFrameInContainer
@ -151,7 +165,16 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
itemViewController.setControlsVisible(false, animated: false, dueToUserInteraction: false) itemViewController.setControlsVisible(false, animated: false, dueToUserInteraction: false)
} }
if let sourceSnapshot {
animator.addAnimations({
self.sourceView.layer.opacity = 1
sourceSnapshot.layer.opacity = 0
}, delayFactor: 0.9)
}
animator.addCompletion { _ in animator.addCompletion { _ in
sourceSnapshot?.removeFromSuperview()
// Having dismissed, we don't need to undo any of the changes to the content VC. // Having dismissed, we don't need to undo any of the changes to the content VC.
transitionContext.completeTransition(true) transitionContext.completeTransition(true)

View File

@ -30,6 +30,25 @@ class GalleryPresentationAnimationController: NSObject, UIViewControllerAnimated
return return
} }
// Try to effectively "fade out" anything that's on top of the source view.
// The 0.05 duration makes this happen faster than the rest of the animation,
// and so less noticeable.
let sourceSnapshot = sourceView.snapshotView(afterScreenUpdates: false)
if let sourceSnapshot {
let snapshotContainer = sourceView.ancestorForInsertingSnapshot
snapshotContainer.addSubview(sourceSnapshot)
let sourceFrameInShapshotContainer = snapshotContainer.convert(sourceView.bounds, from: sourceView)
sourceSnapshot.frame = sourceFrameInShapshotContainer
sourceSnapshot.transform = sourceView.transform
sourceSnapshot.layer.opacity = 0
UIView.animate(withDuration: 0.05) {
sourceSnapshot.layer.opacity = 1
// Also fade out the actual source view underneath the snapshot to try
// and account for semi-transparent images.
self.sourceView.layer.opacity = 0
}
}
let container = transitionContext.containerView let container = transitionContext.containerView
to.view.frame = container.bounds to.view.frame = container.bounds
container.addSubview(to.view) container.addSubview(to.view)
@ -149,20 +168,23 @@ class GalleryPresentationAnimationController: NSObject, UIViewControllerAnimated
itemViewController.setControlsVisible(true, animated: false, dueToUserInteraction: false) itemViewController.setControlsVisible(true, animated: false, dueToUserInteraction: false)
if let sourceToDestTransform { if let sourceToDestTransform {
sourceSnapshot?.transform = sourceToDestTransform
self.sourceView.transform = sourceToDestTransform self.sourceView.transform = sourceToDestTransform
} }
} }
animator.addCompletion { _ in animator.addCompletion { _ in
sourceSnapshot?.removeFromSuperview()
self.sourceView.layer.opacity = 1
if sourceToDestTransform != nil {
self.sourceView.transform = origSourceTransform
}
contentContainer.removeFromSuperview() contentContainer.removeFromSuperview()
dimmingView.removeFromSuperview() dimmingView.removeFromSuperview()
to.view.backgroundColor = .black to.view.backgroundColor = .black
if sourceToDestTransform != nil {
self.sourceView.transform = origSourceTransform
}
// Reset the properties we changed before re-adding the content to the scroll view. // Reset the properties we changed before re-adding the content to the scroll view.
// (I would expect UIScrollView to effectively do this itself, but w/e.) // (I would expect UIScrollView to effectively do this itself, but w/e.)
content.view.transform = origContentTransform content.view.transform = origContentTransform

View File

@ -0,0 +1,26 @@
//
// UIView+Utilities.swift
// GalleryVC
//
// Created by Shadowfacts on 11/24/24.
//
import UIKit
extension UIView {
var ancestorForInsertingSnapshot: UIView {
var view = self
while let superview = view.superview {
if superview.layer.masksToBounds {
return superview
} else if superview is UIScrollView {
return self
} else {
view = superview
}
}
return view
}
}