From 53f6c06a93c5e6c890e8e046ebac2e4ce3b18999 Mon Sep 17 00:00:00 2001 From: Giuseppe Lanza Date: Sun, 19 Nov 2017 15:11:19 +0000 Subject: [PATCH] Reverse animation implemented --- LNZCollectionLayouts.podspec | 4 +- .../project.pbxproj | 6 +- .../Animator/SafariAnimator.swift | 136 --------- .../Base.lproj/Main.storyboard | 64 +++- LNZCollectionLayouts/Info.plist | 4 +- .../Layouts/Animator/SafariAnimator.swift | 282 ++++++++++++++++++ .../SafariModalViewController.swift | 25 ++ .../SafariViewController.swift | 20 +- 8 files changed, 389 insertions(+), 152 deletions(-) delete mode 100644 LNZCollectionLayouts/Animator/SafariAnimator.swift create mode 100644 LNZCollectionLayouts/Layouts/Animator/SafariAnimator.swift create mode 100644 LNZCollectionLayouts/SafariModalViewController.swift diff --git a/LNZCollectionLayouts.podspec b/LNZCollectionLayouts.podspec index b19e6e5..d76d69a 100644 --- a/LNZCollectionLayouts.podspec +++ b/LNZCollectionLayouts.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.platform = :ios - s.version = "1.1.0" + s.version = "1.1.1" s.ios.deployment_target = '8.0' s.name = "LNZCollectionLayouts" s.summary = "A swift collection of UICollectionViewLayout subclasses." @@ -19,7 +19,7 @@ Pod::Spec.new do |s| s.source = { :git => "https://github.com/gringoireDM/LNZCollectionLayouts.git", - :tag => "v1.1.0" + :tag => "v1.1.1" } s.framework = "UIKit" diff --git a/LNZCollectionLayouts.xcodeproj/project.pbxproj b/LNZCollectionLayouts.xcodeproj/project.pbxproj index 1d2607b..75ba7fc 100644 --- a/LNZCollectionLayouts.xcodeproj/project.pbxproj +++ b/LNZCollectionLayouts.xcodeproj/project.pbxproj @@ -16,6 +16,7 @@ 500D6F131F1E32B000FE20D4 /* CollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500D6F121F1E32B000FE20D4 /* CollectionViewController.swift */; }; 500D6F181F1E6E5100FE20D4 /* LNZCarouselCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500D6F161F1E6E0000FE20D4 /* LNZCarouselCollectionViewLayout.swift */; }; 501CF9F31FBFABBD003CAC14 /* SafariAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501CF9F21FBFABBD003CAC14 /* SafariAnimator.swift */; }; + 501CF9F51FC0AEC3003CAC14 /* SafariModalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501CF9F41FC0AEC3003CAC14 /* SafariModalViewController.swift */; }; 5025EE8F1F42EA6C001521F6 /* LNZSafariTabLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5025EE8E1F42EA6C001521F6 /* LNZSafariTabLayout.swift */; }; 5025EE931F43070A001521F6 /* SafariViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5025EE921F43070A001521F6 /* SafariViewController.swift */; }; 50377DA41F574FFB00A567D6 /* LNZSnapToCenterCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500D6F141F1E3D7000FE20D4 /* LNZSnapToCenterCollectionViewLayout.swift */; }; @@ -35,6 +36,7 @@ 500D6F141F1E3D7000FE20D4 /* LNZSnapToCenterCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZSnapToCenterCollectionViewLayout.swift; sourceTree = ""; }; 500D6F161F1E6E0000FE20D4 /* LNZCarouselCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZCarouselCollectionViewLayout.swift; sourceTree = ""; }; 501CF9F21FBFABBD003CAC14 /* SafariAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariAnimator.swift; sourceTree = ""; }; + 501CF9F41FC0AEC3003CAC14 /* SafariModalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariModalViewController.swift; sourceTree = ""; }; 5025EE8E1F42EA6C001521F6 /* LNZSafariTabLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZSafariTabLayout.swift; sourceTree = ""; }; 5025EE921F43070A001521F6 /* SafariViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafariViewController.swift; sourceTree = ""; }; 508E11761F5D585200895B7D /* UICollectionViewDelegateSafariLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewDelegateSafariLayout.swift; sourceTree = ""; }; @@ -75,7 +77,7 @@ 500D6EFF1F1E1CFD00FE20D4 /* ViewController.swift */, 500D6F121F1E32B000FE20D4 /* CollectionViewController.swift */, 5025EE921F43070A001521F6 /* SafariViewController.swift */, - 501CF9F11FBFAB3A003CAC14 /* Animator */, + 501CF9F41FC0AEC3003CAC14 /* SafariModalViewController.swift */, 500D6F011F1E1CFD00FE20D4 /* Main.storyboard */, 500D6F041F1E1CFD00FE20D4 /* Assets.xcassets */, 500D6F061F1E1CFD00FE20D4 /* LaunchScreen.storyboard */, @@ -87,6 +89,7 @@ 500D6F0F1F1E1D2500FE20D4 /* Layouts */ = { isa = PBXGroup; children = ( + 501CF9F11FBFAB3A003CAC14 /* Animator */, 508E11751F5D584400895B7D /* CustomDelegates */, 500D6F141F1E3D7000FE20D4 /* LNZSnapToCenterCollectionViewLayout.swift */, 500D6F101F1E1D3D00FE20D4 /* LNZInfiniteCollectionViewLayout.swift */, @@ -187,6 +190,7 @@ buildActionMask = 2147483647; files = ( 500D6F131F1E32B000FE20D4 /* CollectionViewController.swift in Sources */, + 501CF9F51FC0AEC3003CAC14 /* SafariModalViewController.swift in Sources */, 500D6F001F1E1CFD00FE20D4 /* ViewController.swift in Sources */, 5025EE931F43070A001521F6 /* SafariViewController.swift in Sources */, 508E11771F5D585200895B7D /* UICollectionViewDelegateSafariLayout.swift in Sources */, diff --git a/LNZCollectionLayouts/Animator/SafariAnimator.swift b/LNZCollectionLayouts/Animator/SafariAnimator.swift deleted file mode 100644 index 7f52a1e..0000000 --- a/LNZCollectionLayouts/Animator/SafariAnimator.swift +++ /dev/null @@ -1,136 +0,0 @@ -// -// SafariAnimator.swift -// LNZCollectionLayouts -// -// Created by Giuseppe Lanza on 17/11/2017. -// Copyright © 2017 Gilt. All rights reserved. -// - -import UIKit - -public protocol SafariLayoutContaining { - var safariCollectionView: UICollectionView { get } -} - -public class SafariAnimator: NSObject, UIViewControllerAnimatedTransitioning { - public let presentingIndexPath: IndexPath - public var reversed: Bool = false - - private let animationDuration: TimeInterval = 1 - private let transformForItem: (_ origin: CGPoint, _ size: CGSize, _ angle: CGFloat) -> CATransform3D - - public init(presentingIndexPath: IndexPath, transformFunction: @escaping (_ origin: CGPoint, _ size: CGSize, _ angle: CGFloat) -> CATransform3D) { - self.presentingIndexPath = presentingIndexPath - self.transformForItem = transformFunction - - super.init() - } - - public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { - return animationDuration - } - - public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { - if !reversed { - performTransition(using: transitionContext) - } else { - performReversed(using: transitionContext) - } - } - - private func performTransition(using transitionContext: UIViewControllerContextTransitioning) { - guard let from = transitionContext.viewController(forKey: .from) else { return } - - var safariController: SafariLayoutContaining! = from as? SafariViewController - if safariController == nil { - safariController = from.childViewControllers.last as? SafariViewController - } - - assert(safariController != nil) - - let containerView = transitionContext.containerView - containerView.backgroundColor = .clear - - var frame = safariController.safariCollectionView.superview!.convert(safariController.safariCollectionView.frame, to: containerView) - - var contentInset = safariController.safariCollectionView.contentInset - if #available(iOS 11.0, *) { - contentInset = safariController.safariCollectionView.adjustedContentInset - } - - frame.origin.y += contentInset.top - frame.size.height -= contentInset.top - contentInset.bottom - - let mockCollectionView = UIView(frame: frame) - mockCollectionView.backgroundColor = safariController.safariCollectionView.backgroundColor - mockCollectionView.clipsToBounds = true - - containerView.addSubview(mockCollectionView) - - var mockCells = [IndexPath: UIView]() - - let visibleCellsMap = safariController.safariCollectionView.indexPathsForVisibleItems.reduce([IndexPath: UICollectionViewCell]()) { - var mutableInitial = $0 - mutableInitial[$1] = safariController.safariCollectionView.cellForItem(at: $1) - return mutableInitial - } - - for (indexPath, cell) in visibleCellsMap { - guard let cellClone = cell.snapshotView(afterScreenUpdates: true) else { continue } - let transform = cell.layer.transform - - cell.layer.transform = CATransform3DIdentity - - cellClone.frame = safariController.safariCollectionView.convert(cell.frame, to: mockCollectionView) - cellClone.layer.transform = transform - cell.layer.transform = transform - - mockCollectionView.addSubview(cellClone) - mockCells[indexPath] = cellClone - } - - safariController.safariCollectionView.alpha = 0 - - let cellAnimation = {(indexPath: IndexPath, cell: UIView) in - guard indexPath.item != self.presentingIndexPath.item else { return } - - let size = cell.bounds.size - var targetOrigin: CGPoint! - - if indexPath.item < self.presentingIndexPath.item { - targetOrigin = CGPoint(x: 0, y: 0) - } else if indexPath.item > self.presentingIndexPath.item{ - targetOrigin = CGPoint(x: 0, y: mockCollectionView.bounds.height) - } - - cell.frame = CGRect(origin: targetOrigin, size: size) - cell.layer.transform = self.transformForItem(targetOrigin, size, -.pi/2) - cell.alpha = 0 - } - - UIView.animate(withDuration: animationDuration/2.0, animations: { - for (indexPath, cell) in mockCells { - cellAnimation(indexPath, cell) - } - }) {(finished) in - mockCollectionView.clipsToBounds = false - } - - UIView.animate(withDuration: animationDuration, animations: { - let presentingCell = mockCells[self.presentingIndexPath] - presentingCell?.layer.transform = CATransform3DIdentity - presentingCell?.frame = containerView.convert(containerView.bounds, to: mockCollectionView) - - for (indexPath, cell) in mockCells.filter({ $0.key.item < self.presentingIndexPath.item }) { - cellAnimation(indexPath, cell) - } - }) { (finished) in - mockCollectionView.removeFromSuperview() - transitionContext.completeTransition(finished) - } - } - - private func performReversed(using transitionContext: UIViewControllerContextTransitioning) { - - } -} diff --git a/LNZCollectionLayouts/Base.lproj/Main.storyboard b/LNZCollectionLayouts/Base.lproj/Main.storyboard index 2b7e83c..2f6ea37 100644 --- a/LNZCollectionLayouts/Base.lproj/Main.storyboard +++ b/LNZCollectionLayouts/Base.lproj/Main.storyboard @@ -15,8 +15,8 @@ - - + + @@ -141,7 +141,7 @@ - + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. @@ -160,6 +160,9 @@ + + + @@ -170,7 +173,60 @@ - + + + + + + + + + + + + + + + + + + + Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda. + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/LNZCollectionLayouts/Info.plist b/LNZCollectionLayouts/Info.plist index 9bda8d4..af80c10 100644 --- a/LNZCollectionLayouts/Info.plist +++ b/LNZCollectionLayouts/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.1.0 + 1.1.1 CFBundleVersion - 5 + 6 LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/LNZCollectionLayouts/Layouts/Animator/SafariAnimator.swift b/LNZCollectionLayouts/Layouts/Animator/SafariAnimator.swift new file mode 100644 index 0000000..4856c46 --- /dev/null +++ b/LNZCollectionLayouts/Layouts/Animator/SafariAnimator.swift @@ -0,0 +1,282 @@ +// +// SafariAnimator.swift +// LNZCollectionLayouts +// +// Created by Giuseppe Lanza on 17/11/2017. +// Copyright © 2017 Gilt. All rights reserved. +// + +import UIKit + +public protocol SafariLayoutContaining { + var safariCollectionView: UICollectionView { get } +} + +/** + This transition will animate a specified collectionViewCell in an LNZSafariLayout in order to be perspectically + corrected to be shown as presented ViewController. The animation includes an interpolation between the content + of the collectionViewCell and the content of the presented ViewController. + */ +public class SafariAnimator: NSObject, UIViewControllerAnimatedTransitioning { + ///The indexPath of the cell that is going to be animated to be presented as modal view controller + public let presentingIndexPath: IndexPath + + ///Set to true this property if you want to use this transition for dismissing controllers + public var reversed: Bool = false + + ///The duration of the whole animation + public var animationDuration: TimeInterval = 1 + + private let transformForItem: (_ origin: CGPoint, _ size: CGSize, _ angle: CGFloat) -> CATransform3D + + /** + Initialize a SafariAnimator object. The indexPAth to be presented and the transform function must be provided. + - parameter presentingIndexPath: The indexPath of the cell that is going to be presented as new modal ViewController + - parameter transformFunction: the function returning the 3d Transform matrix to manipulate the cells inclination + in the collection + - parameter origin: The origin point of the cell that is going to be transformed. This value is an absolute value + fetched when the cell has as transform matrix the identity matrix. + - parameter size: The size of the cell that is going to be transformed. This value is an absolute value fetched + when the cell has as transform matrix the identity matrix. + */ + public init(presentingIndexPath: IndexPath, transformFunction: @escaping (_ origin: CGPoint, _ size: CGSize, _ angle: CGFloat) -> CATransform3D) { + self.presentingIndexPath = presentingIndexPath + self.transformForItem = transformFunction + + super.init() + } + + public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return animationDuration + } + + public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + if !reversed { + performTransition(using: transitionContext) + } else { + performReversed(using: transitionContext) + } + } + + ///This method generates a mock collectionView that will be overlapped to the real safari collection view. + ///This mock will be the container for the whole animation. + private func setupMockCollection(inContainer containerView: UIView, from safariController: SafariLayoutContaining) -> UIView { + var frame = safariController.safariCollectionView.superview!.convert(safariController.safariCollectionView.frame, to: containerView) + + var contentInset = safariController.safariCollectionView.contentInset + if #available(iOS 11.0, *) { + contentInset = safariController.safariCollectionView.adjustedContentInset + } + + frame.origin.y += contentInset.top + frame.size.height -= contentInset.top - contentInset.bottom + + let mockCollectionView = UIView(frame: frame) + mockCollectionView.backgroundColor = safariController.safariCollectionView.backgroundColor + mockCollectionView.clipsToBounds = true + + containerView.addSubview(mockCollectionView) + return mockCollectionView + } + + ///This method returns the cells that are currently visible in the collectionView + private func visibleCells(in safariController: SafariLayoutContaining) -> [IndexPath: UICollectionViewCell] { + return safariController.safariCollectionView.indexPathsForVisibleItems.reduce([IndexPath: UICollectionViewCell]()) { + var mutableInitial = $0 + mutableInitial[$1] = safariController.safariCollectionView.cellForItem(at: $1) + return mutableInitial + } + } + + ///This method will perform the actual animation for the present modal + private func performTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard let from = transitionContext.viewController(forKey: .from), + let to = transitionContext.viewController(forKey: .to) else { return } + + to.view.alpha = 0 + + var safariController: SafariLayoutContaining! = from as? SafariLayoutContaining + if safariController == nil { + safariController = from.childViewControllers.last as? SafariLayoutContaining + } + + //This Transition must be performed from a SafariLayoutContaining viewController to a ViewController + assert(safariController != nil, "This Transition must be performed from a SafariLayoutContaining viewController to a ViewController") + + let containerView = transitionContext.containerView + containerView.backgroundColor = .clear + + let mockCollectionView = setupMockCollection(inContainer: containerView, from: safariController) + + var mockCells = [IndexPath: UIView]() + + let visibleCellsMap = visibleCells(in: safariController) + + //Here we must generate mocks for the visible cells in the collection view, to animate them. + //We don't want to manipulate real cell frames outside of the layout's context, as it is the layout + //job to do that + for (indexPath, cell) in visibleCellsMap { + guard let cellClone = cell.snapshotView(afterScreenUpdates: true) else { continue } + let transform = cell.layer.transform + + //To fetch the real frame of the cell we need to temporary remove its transform because it might + //alter the final bounds. + cell.layer.transform = CATransform3DIdentity + + //not the cell.frame property reflects perfectly the real frame of the cell in the collectionView + //before the transform is applied. + cellClone.frame = safariController.safariCollectionView.convert(cell.frame, to: mockCollectionView) + + //Now we are ready to apply the transform to the mock cell, and re apply it to the original cell. + cellClone.layer.transform = transform + cell.layer.transform = transform + + //If the cell is the one we want to animate we need to add the view of the destination view controller to it + //to allow the interpolation effect during the animation. + if indexPath == presentingIndexPath { + to.view.frame = cellClone.bounds + to.view.frame.origin = .zero + + cellClone.addSubview(to.view) + to.view.setNeedsLayout() + to.view.layoutIfNeeded() + } + + mockCollectionView.addSubview(cellClone) + mockCells[indexPath] = cellClone + } + + //The real collection now must be hidden. The mockCollectionView might be transparent at this point and if we + //animate the mock cells we will most certainly see the real cells under... we don't want that. + safariController.safariCollectionView.alpha = 0 + + //Ready to animate + UIView.animate(withDuration: animationDuration/2.0, animations: { + for (indexPath, cell) in mockCells { + self.cellOpenTransform(indexPath: indexPath, cell: cell, inRect: mockCollectionView.bounds) + } + }) {(finished) in + + //At this point the first part of the animation is finished. All the cells that are not the ones that should + //be animated are now transparent, but we want the presenting cell to go full screen and the mock collection + //view might be not full screen. We cannot add the presenting mock cell on top of the mockCollection as + //subView of the container view because the perspective transform adjustment would create an unexpected view + //hierarchy with as consequence a really bad user experience. + mockCollectionView.clipsToBounds = false + } + + UIView.animate(withDuration: animationDuration, animations: { + let presentingCell = mockCells[self.presentingIndexPath] + presentingCell?.layer.transform = CATransform3DIdentity + presentingCell?.frame = containerView.convert(containerView.bounds, to: mockCollectionView) + + to.view.alpha = 1 + }) { (finished) in + containerView.addSubview(to.view) + to.view.layer.mask = nil + + mockCollectionView.removeFromSuperview() + transitionContext.completeTransition(finished) + + safariController.safariCollectionView.alpha = 1 + } + } + + private func cellOpenTransform(indexPath: IndexPath, cell: UIView, inRect rect: CGRect) { + guard indexPath.item != self.presentingIndexPath.item else { return } + + let size = cell.bounds.size + var targetOrigin: CGPoint! + + if indexPath.item < self.presentingIndexPath.item { + targetOrigin = CGPoint(x: 0, y: 0) + } else if indexPath.item > self.presentingIndexPath.item{ + targetOrigin = CGPoint(x: 0, y: rect.height) + } + + cell.frame = CGRect(origin: targetOrigin, size: size) + cell.layer.transform = self.transformForItem(targetOrigin, size, -.pi/2) + cell.alpha = 0 + + } + + private func performReversed(using transitionContext: UIViewControllerContextTransitioning) { + guard let from = transitionContext.viewController(forKey: .from), + let to = transitionContext.viewController(forKey: .to), + let fromClone = from.view.snapshotView(afterScreenUpdates: true) else { return } + + from.view.removeFromSuperview() + + var safariController: SafariLayoutContaining! = to as? SafariLayoutContaining + if safariController == nil { + safariController = to.childViewControllers.last as? SafariLayoutContaining + } + + //This Transition must be performed from a ViewController to a SafariLayoutContaining viewController + assert(safariController != nil, "This Transition must be performed from a ViewController to a SafariLayoutContaining viewController") + + let containerView = transitionContext.containerView + containerView.backgroundColor = .clear + + let mockCollectionView = setupMockCollection(inContainer: containerView, from: safariController) + mockCollectionView.clipsToBounds = false + + var mockCells = [IndexPath: (cell: UIView, originalFrame: CGRect, originalTransform: CATransform3D)]() + + if !safariController.safariCollectionView.indexPathsForVisibleItems.contains(presentingIndexPath) { + safariController.safariCollectionView.scrollToItem(at: presentingIndexPath, at: .centeredVertically, animated: false) + } + let visibleCellsMap = visibleCells(in: safariController) + + for (indexPath, cell) in visibleCellsMap { + guard let cellClone = cell.snapshotView(afterScreenUpdates: true) else { continue } + let originalTransform = cell.layer.transform + + cell.layer.transform = CATransform3DIdentity + let originalFrame = safariController.safariCollectionView.convert(cell.frame, to: mockCollectionView) + + cell.layer.transform = originalTransform + if indexPath == presentingIndexPath { + cellClone.frame = containerView.convert(containerView.bounds, to: mockCollectionView) + cellClone.layer.transform = CATransform3DIdentity + fromClone.frame = from.view.bounds + cellClone.addSubview(fromClone) + } else { + cellOpenTransform(indexPath: indexPath, cell: cellClone, inRect: mockCollectionView.bounds) + } + + mockCollectionView.addSubview(cellClone) + + mockCells[indexPath] = (cellClone, originalFrame, originalTransform) + } + safariController.safariCollectionView.alpha = 0 + + DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration/2) { + mockCollectionView.clipsToBounds = true + + UIView.animate(withDuration: self.animationDuration/2) { + var cells = mockCells + cells[self.presentingIndexPath] = nil + for (cell, originalFrame, originalTransform) in cells.values { + cell.frame = originalFrame + cell.layer.transform = originalTransform + cell.alpha = 1 + } + } + } + + + UIView.animate(withDuration: animationDuration, animations: { + let presentingCell = mockCells[self.presentingIndexPath] + presentingCell?.cell.layer.transform = presentingCell!.originalTransform + presentingCell?.cell.frame = presentingCell!.originalFrame + + fromClone.alpha = 0 + }) { (finished) in + safariController.safariCollectionView.alpha = 1 + + mockCollectionView.removeFromSuperview() + transitionContext.completeTransition(finished) + } + } +} diff --git a/LNZCollectionLayouts/SafariModalViewController.swift b/LNZCollectionLayouts/SafariModalViewController.swift new file mode 100644 index 0000000..75a0379 --- /dev/null +++ b/LNZCollectionLayouts/SafariModalViewController.swift @@ -0,0 +1,25 @@ +// +// SafariModalViewController.swift +// LNZCollectionLayouts +// +// Created by Giuseppe Lanza on 18/11/2017. +// Copyright © 2017 Gilt. All rights reserved. +// + +import UIKit + +class SafariModalViewController: UIViewController { + var presentedElement: Int! + + + @IBOutlet weak var elementTitle: UILabel! + + override func viewDidLoad() { + super.viewDidLoad() + elementTitle.text = "\(presentedElement!)" + } + + @IBAction func close(_ sender: Any) { + dismiss(animated: true, completion: nil) + } +} diff --git a/LNZCollectionLayouts/SafariViewController.swift b/LNZCollectionLayouts/SafariViewController.swift index de75300..44c514b 100644 --- a/LNZCollectionLayouts/SafariViewController.swift +++ b/LNZCollectionLayouts/SafariViewController.swift @@ -59,13 +59,14 @@ class SafariViewController: UICollectionViewController, UICollectionViewDelegate collectionView?.collectionViewLayout.invalidateLayout() } - var animator: SafariAnimator? - override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - modalPresentationStyle = .custom + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + guard segue.identifier == "presentTabSegue", + let detailController = segue.destination as? SafariModalViewController, + let indexPath = collectionView?.indexPathsForSelectedItems?.first else { return } - let controller = UIViewController() - controller.transitioningDelegate = self - present(controller, animated: true, completion: nil) + let el = elements[indexPath.item] + detailController.transitioningDelegate = self + detailController.presentedElement = el } } @@ -76,6 +77,11 @@ extension SafariViewController: UIViewControllerTransitioningDelegate { } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { - return nil + guard let element = (dismissed as? SafariModalViewController)?.presentedElement, + let item = elements.index(of: element) else { return nil } + let indexPath = IndexPath(item: item, section: 0) + let animator = (collectionView?.collectionViewLayout as? LNZSafariLayout)?.animator(forItem: indexPath) + animator?.reversed = true + return animator } }