Skip to content

Commit

Permalink
Features/imageviewer transition (michaelhenry#104)
Browse files Browse the repository at this point in the history
used of dedicated transition.
  • Loading branch information
michaelhenry committed Aug 19, 2020
1 parent a7026c0 commit a080488
Show file tree
Hide file tree
Showing 5 changed files with 320 additions and 195 deletions.
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

0 comments on commit a080488

Please sign in to comment.