Skip to content

Commit a080488

Browse files
authored
Features/imageviewer transition (#104)
used of dedicated transition.
1 parent a7026c0 commit a080488

File tree

5 files changed

+320
-195
lines changed

5 files changed

+320
-195
lines changed

Example/Demo.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
A6BC0BE023913C66004A4E46 /* GalleryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BDF23913C66004A4E46 /* GalleryViewController.swift */; };
2020
A6BC0BE223914239004A4E46 /* ThumbCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BE123914239004A4E46 /* ThumbCell.swift */; };
2121
A6BC0BE823915420004A4E46 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6BC0BE723915420004A4E46 /* Data.swift */; };
22+
A6FE4B4624EDDBAE00E9C754 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = A6FE4B4524EDDBAE00E9C754 /* README.md */; };
2223
D303414D23CA5F17FEDDCF7B /* Pods_Demo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 10710A5B07A80798DED8269E /* Pods_Demo.framework */; };
2324
/* End PBXBuildFile section */
2425

@@ -39,6 +40,7 @@
3940
A6BC0BDF23913C66004A4E46 /* GalleryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GalleryViewController.swift; sourceTree = "<group>"; };
4041
A6BC0BE123914239004A4E46 /* ThumbCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThumbCell.swift; sourceTree = "<group>"; };
4142
A6BC0BE723915420004A4E46 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
43+
A6FE4B4524EDDBAE00E9C754 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../../README.md; sourceTree = "<group>"; };
4244
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>"; };
4345
/* End PBXFileReference section */
4446

@@ -83,6 +85,7 @@
8385
A6BC0BC6239139F8004A4E46 /* Demo */ = {
8486
isa = PBXGroup;
8587
children = (
88+
A6FE4B4524EDDBAE00E9C754 /* README.md */,
8689
A6BC0BC7239139F8004A4E46 /* AppDelegate.swift */,
8790
A6BC0BD0239139F9004A4E46 /* Assets.xcassets */,
8891
A6BC0BD2239139F9004A4E46 /* LaunchScreen.storyboard */,
@@ -169,6 +172,7 @@
169172
isa = PBXResourcesBuildPhase;
170173
buildActionMask = 2147483647;
171174
files = (
175+
A6FE4B4624EDDBAE00E9C754 /* README.md in Resources */,
172176
A6BC0BD4239139F9004A4E46 /* LaunchScreen.storyboard in Resources */,
173177
A6BC0BD1239139F9004A4E46 /* Assets.xcassets in Resources */,
174178
);
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
//
2+
// ImageViewerTransitionPresentationManager.swift
3+
// ImageViewer.swift
4+
//
5+
// Created by Michael Henry Pantaleon on 2020/08/19.
6+
//
7+
8+
import Foundation
9+
import UIKit
10+
11+
protocol ImageViewerTransitionViewControllerConvertible {
12+
13+
// The source view
14+
var sourceView: UIImageView? { get }
15+
16+
// The final view
17+
var targetView: UIImageView? { get }
18+
}
19+
20+
final class ImageViewerTransitionPresentationAnimator:NSObject {
21+
22+
let isPresenting: Bool
23+
24+
init(isPresenting: Bool) {
25+
self.isPresenting = isPresenting
26+
super.init()
27+
}
28+
}
29+
30+
// MARK: - UIViewControllerAnimatedTransitioning
31+
extension ImageViewerTransitionPresentationAnimator: UIViewControllerAnimatedTransitioning {
32+
33+
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?)
34+
-> TimeInterval {
35+
return 0.3
36+
}
37+
38+
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
39+
let key: UITransitionContextViewControllerKey = isPresenting ? .to : .from
40+
guard let controller = transitionContext.viewController(forKey: key)
41+
else { return }
42+
43+
let animationDuration = transitionDuration(using: transitionContext)
44+
45+
if isPresenting {
46+
presentAnimation(
47+
transitionView: transitionContext.containerView,
48+
controller: controller,
49+
duration: animationDuration) { finished in
50+
transitionContext.completeTransition(finished)
51+
}
52+
53+
} else {
54+
dismissAnimation(
55+
transitionView: transitionContext.containerView,
56+
controller: controller,
57+
duration: animationDuration) { finished in
58+
transitionContext.completeTransition(finished)
59+
}
60+
}
61+
}
62+
63+
private func createDummyImageView(frame: CGRect, image:UIImage? = nil)
64+
-> UIImageView {
65+
let dummyImageView:UIImageView = UIImageView(frame: frame)
66+
dummyImageView.clipsToBounds = true
67+
dummyImageView.contentMode = .scaleAspectFill
68+
dummyImageView.alpha = 1.0
69+
dummyImageView.image = image
70+
return dummyImageView
71+
}
72+
73+
private func presentAnimation(
74+
transitionView:UIView,
75+
controller: UIViewController,
76+
duration: TimeInterval,
77+
completed: @escaping((Bool) -> Void)) {
78+
79+
guard
80+
let transitionVC = controller as? ImageViewerTransitionViewControllerConvertible
81+
else { return }
82+
83+
let sourceView = transitionVC.sourceView
84+
85+
let dummyImageView = createDummyImageView(
86+
frame: sourceView?.frameRelativeToWindow() ?? .zero,
87+
image: sourceView?.image)
88+
transitionView.addSubview(dummyImageView)
89+
90+
sourceView?.alpha = 0.0
91+
92+
transitionView.addSubview(controller.view)
93+
controller.view.alpha = 0.0
94+
95+
UIView.animate(withDuration: duration, animations: {
96+
dummyImageView.contentMode = .scaleAspectFit
97+
dummyImageView.frame = UIScreen.main.bounds
98+
controller.view.alpha = 1.0
99+
}) { finished in
100+
dummyImageView.removeFromSuperview()
101+
completed(finished)
102+
}
103+
}
104+
105+
private func dismissAnimation(
106+
transitionView:UIView,
107+
controller: UIViewController,
108+
duration:TimeInterval,
109+
completed: @escaping((Bool) -> Void)) {
110+
111+
guard
112+
let transitionVC = controller as? ImageViewerTransitionViewControllerConvertible
113+
else { return }
114+
115+
let sourceView = transitionVC.sourceView
116+
let targetView = transitionVC.targetView
117+
118+
let dummyImageView = createDummyImageView(
119+
frame: targetView?.frameRelativeToWindow() ?? UIScreen.main.bounds,
120+
image: targetView?.image)
121+
transitionView.addSubview(dummyImageView)
122+
targetView?.isHidden = true
123+
124+
controller.view.alpha = 1.0
125+
UIView.animate(withDuration: duration, animations: {
126+
if let sourceView = sourceView {
127+
// return to original position
128+
dummyImageView.frame = sourceView.frameRelativeToWindow()
129+
} else {
130+
// just disappear
131+
dummyImageView.alpha = 0.0
132+
}
133+
controller.view.alpha = 0.0
134+
}) { finished in
135+
sourceView?.alpha = 1.0
136+
controller.view.removeFromSuperview()
137+
completed(finished)
138+
}
139+
}
140+
}
141+
142+
final class ImageViewerTransitionPresentationController: UIPresentationController {
143+
144+
override var frameOfPresentedViewInContainerView: CGRect {
145+
var frame: CGRect = .zero
146+
frame.size = size(forChildContentContainer: presentedViewController,
147+
withParentContainerSize: containerView!.bounds.size)
148+
return frame
149+
}
150+
151+
override func containerViewWillLayoutSubviews() {
152+
presentedView?.frame = frameOfPresentedViewInContainerView
153+
}
154+
}
155+
156+
final class ImageViewerTransitionPresentationManager: NSObject {
157+
158+
}
159+
160+
// MARK: - UIViewControllerTransitioningDelegate
161+
extension ImageViewerTransitionPresentationManager: UIViewControllerTransitioningDelegate {
162+
func presentationController(
163+
forPresented presented: UIViewController,
164+
presenting: UIViewController?,
165+
source: UIViewController
166+
) -> UIPresentationController? {
167+
let presentationController = ImageViewerTransitionPresentationController(
168+
presentedViewController: presented,
169+
presenting: presenting)
170+
return presentationController
171+
}
172+
173+
func animationController(
174+
forPresented presented: UIViewController,
175+
presenting: UIViewController,
176+
source: UIViewController
177+
) -> UIViewControllerAnimatedTransitioning? {
178+
179+
return ImageViewerTransitionPresentationAnimator(isPresenting: true)
180+
}
181+
182+
func animationController(
183+
forDismissed dismissed: UIViewController
184+
) -> UIViewControllerAnimatedTransitioning? {
185+
return ImageViewerTransitionPresentationAnimator(isPresenting: false)
186+
}
187+
}
188+
189+
// MARK: - UIAdaptivePresentationControllerDelegate
190+
extension ImageViewerTransitionPresentationManager: UIAdaptivePresentationControllerDelegate {
191+
192+
func adaptivePresentationStyle(
193+
for controller: UIPresentationController,
194+
traitCollection: UITraitCollection
195+
) -> UIModalPresentationStyle {
196+
return .none
197+
}
198+
199+
func presentationController(
200+
_ controller: UIPresentationController,
201+
viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle
202+
) -> UIViewController? {
203+
return nil
204+
}
205+
}

0 commit comments

Comments
 (0)