Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Example/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
A6BC0BE023913C66004A4E46 /* GalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BDF23913C66004A4E46 /* GalleryViewController.swift */; };
A6BC0BE223914239004A4E46 /* ThumbCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BE123914239004A4E46 /* ThumbCell.swift */; };
A6BC0BE823915420004A4E46 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BE723915420004A4E46 /* Data.swift */; };
A6FE4B4624EDDBAE00E9C754 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = A6FE4B4524EDDBAE00E9C754 /* README.md */; };
D303414D23CA5F17FEDDCF7B /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 10710A5B07A80798DED8269E /* Pods_Demo.framework */; };
/* End PBXBuildFile section */

Expand All @@ -39,6 +40,7 @@
A6BC0BDF23913C66004A4E46 /* GalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewController.swift; sourceTree = "<group>"; };
A6BC0BE123914239004A4E46 /* ThumbCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbCell.swift; sourceTree = "<group>"; };
A6BC0BE723915420004A4E46 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
A6FE4B4524EDDBAE00E9C754 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../README.md; sourceTree = "<group>"; };
D023B1BBDAD100A2962FDC7E /* Pods-Demo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Demo.debug.xcconfig"; path = "Target Support Files/Pods-Demo/Pods-Demo.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */

Expand Down Expand Up @@ -83,6 +85,7 @@
A6BC0BC6239139F8004A4E46 /* Demo */ = {
isa = PBXGroup;
children = (
A6FE4B4524EDDBAE00E9C754 /* README.md */,
A6BC0BC7239139F8004A4E46 /* AppDelegate.swift */,
A6BC0BD0239139F9004A4E46 /* Assets.xcassets */,
A6BC0BD2239139F9004A4E46 /* LaunchScreen.storyboard */,
Expand Down Expand Up @@ -169,6 +172,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
A6FE4B4624EDDBAE00E9C754 /* README.md in Resources */,
A6BC0BD4239139F9004A4E46 /* LaunchScreen.storyboard in Resources */,
A6BC0BD1239139F9004A4E46 /* Assets.xcassets in Resources */,
);
Expand Down
205 changes: 205 additions & 0 deletions ImageViewerTransitionPresentationManager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
//
// ImageViewerTransitionPresentationManager.swift
// ImageViewer.swift
//
// Created by Michael Henry Pantaleon on 2020/08/19.
//

import Foundation
import UIKit

protocol ImageViewerTransitionViewControllerConvertible {

// The source view
var sourceView: UIImageView? { get }

// The final view
var targetView: UIImageView? { get }
}

final class ImageViewerTransitionPresentationAnimator:NSObject {

let isPresenting: Bool

init(isPresenting: Bool) {
self.isPresenting = isPresenting
super.init()
}
}

// MARK: - UIViewControllerAnimatedTransitioning
extension ImageViewerTransitionPresentationAnimator: UIViewControllerAnimatedTransitioning {

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?)
-> TimeInterval {
return 0.3
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let key: UITransitionContextViewControllerKey = isPresenting ? .to : .from
guard let controller = transitionContext.viewController(forKey: key)
else { return }

let animationDuration = transitionDuration(using: transitionContext)

if isPresenting {
presentAnimation(
transitionView: transitionContext.containerView,
controller: controller,
duration: animationDuration) { finished in
transitionContext.completeTransition(finished)
}

} else {
dismissAnimation(
transitionView: transitionContext.containerView,
controller: controller,
duration: animationDuration) { finished in
transitionContext.completeTransition(finished)
}
}
}

private func createDummyImageView(frame: CGRect, image:UIImage? = nil)
-> UIImageView {
let dummyImageView:UIImageView = UIImageView(frame: frame)
dummyImageView.clipsToBounds = true
dummyImageView.contentMode = .scaleAspectFill
dummyImageView.alpha = 1.0
dummyImageView.image = image
return dummyImageView
}

private func presentAnimation(
transitionView:UIView,
controller: UIViewController,
duration: TimeInterval,
completed: @escaping((Bool) -> Void)) {

guard
let transitionVC = controller as? ImageViewerTransitionViewControllerConvertible
else { return }

let sourceView = transitionVC.sourceView

let dummyImageView = createDummyImageView(
frame: sourceView?.frameRelativeToWindow() ?? .zero,
image: sourceView?.image)
transitionView.addSubview(dummyImageView)

sourceView?.alpha = 0.0

transitionView.addSubview(controller.view)
controller.view.alpha = 0.0

UIView.animate(withDuration: duration, animations: {
dummyImageView.contentMode = .scaleAspectFit
dummyImageView.frame = UIScreen.main.bounds
controller.view.alpha = 1.0
}) { finished in
dummyImageView.removeFromSuperview()
completed(finished)
}
}

private func dismissAnimation(
transitionView:UIView,
controller: UIViewController,
duration:TimeInterval,
completed: @escaping((Bool) -> Void)) {

guard
let transitionVC = controller as? ImageViewerTransitionViewControllerConvertible
else { return }

let sourceView = transitionVC.sourceView
let targetView = transitionVC.targetView

let dummyImageView = createDummyImageView(
frame: targetView?.frameRelativeToWindow() ?? UIScreen.main.bounds,
image: targetView?.image)
transitionView.addSubview(dummyImageView)
targetView?.isHidden = true

controller.view.alpha = 1.0
UIView.animate(withDuration: duration, animations: {
if let sourceView = sourceView {
// return to original position
dummyImageView.frame = sourceView.frameRelativeToWindow()
} else {
// just disappear
dummyImageView.alpha = 0.0
}
controller.view.alpha = 0.0
}) { finished in
sourceView?.alpha = 1.0
controller.view.removeFromSuperview()
completed(finished)
}
}
}

final class ImageViewerTransitionPresentationController: UIPresentationController {

override var frameOfPresentedViewInContainerView: CGRect {
var frame: CGRect = .zero
frame.size = size(forChildContentContainer: presentedViewController,
withParentContainerSize: containerView!.bounds.size)
return frame
}

override func containerViewWillLayoutSubviews() {
presentedView?.frame = frameOfPresentedViewInContainerView
}
}

final class ImageViewerTransitionPresentationManager: NSObject {

}

// MARK: - UIViewControllerTransitioningDelegate
extension ImageViewerTransitionPresentationManager: UIViewControllerTransitioningDelegate {
func presentationController(
forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController
) -> UIPresentationController? {
let presentationController = ImageViewerTransitionPresentationController(
presentedViewController: presented,
presenting: presenting)
return presentationController
}

func animationController(
forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController
) -> UIViewControllerAnimatedTransitioning? {

return ImageViewerTransitionPresentationAnimator(isPresenting: true)
}

func animationController(
forDismissed dismissed: UIViewController
) -> UIViewControllerAnimatedTransitioning? {
return ImageViewerTransitionPresentationAnimator(isPresenting: false)
}
}

// MARK: - UIAdaptivePresentationControllerDelegate
extension ImageViewerTransitionPresentationManager: UIAdaptivePresentationControllerDelegate {

func adaptivePresentationStyle(
for controller: UIPresentationController,
traitCollection: UITraitCollection
) -> UIModalPresentationStyle {
return .none
}

func presentationController(
_ controller: UIPresentationController,
viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle
) -> UIViewController? {
return nil
}
}
Loading