Skip to content

Commit

Permalink
Created custom animator to handle the transition between the safari v…
Browse files Browse the repository at this point in the history
…iew controller to a view controller.
  • Loading branch information
gringoireDM committed Nov 18, 2017
1 parent f094028 commit eb9cbb8
Show file tree
Hide file tree
Showing 4 changed files with 244 additions and 50 deletions.
12 changes: 12 additions & 0 deletions LNZCollectionLayouts.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
500D6F111F1E1D3D00FE20D4 /* LNZInfiniteCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 500D6F101F1E1D3D00FE20D4 /* LNZInfiniteCollectionViewLayout.swift */; };
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 */; };
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 */; };
Expand All @@ -33,6 +34,7 @@
500D6F121F1E32B000FE20D4 /* CollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewController.swift; sourceTree = "<group>"; };
500D6F141F1E3D7000FE20D4 /* LNZSnapToCenterCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZSnapToCenterCollectionViewLayout.swift; sourceTree = "<group>"; };
500D6F161F1E6E0000FE20D4 /* LNZCarouselCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZCarouselCollectionViewLayout.swift; sourceTree = "<group>"; };
501CF9F21FBFABBD003CAC14 /* SafariAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariAnimator.swift; sourceTree = "<group>"; };
5025EE8E1F42EA6C001521F6 /* LNZSafariTabLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LNZSafariTabLayout.swift; sourceTree = "<group>"; };
5025EE921F43070A001521F6 /* SafariViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SafariViewController.swift; sourceTree = "<group>"; };
508E11761F5D585200895B7D /* UICollectionViewDelegateSafariLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UICollectionViewDelegateSafariLayout.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -73,6 +75,7 @@
500D6EFF1F1E1CFD00FE20D4 /* ViewController.swift */,
500D6F121F1E32B000FE20D4 /* CollectionViewController.swift */,
5025EE921F43070A001521F6 /* SafariViewController.swift */,
501CF9F11FBFAB3A003CAC14 /* Animator */,
500D6F011F1E1CFD00FE20D4 /* Main.storyboard */,
500D6F041F1E1CFD00FE20D4 /* Assets.xcassets */,
500D6F061F1E1CFD00FE20D4 /* LaunchScreen.storyboard */,
Expand All @@ -93,6 +96,14 @@
path = Layouts;
sourceTree = "<group>";
};
501CF9F11FBFAB3A003CAC14 /* Animator */ = {
isa = PBXGroup;
children = (
501CF9F21FBFABBD003CAC14 /* SafariAnimator.swift */,
);
path = Animator;
sourceTree = "<group>";
};
508E11751F5D584400895B7D /* CustomDelegates */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -183,6 +194,7 @@
500D6F111F1E1D3D00FE20D4 /* LNZInfiniteCollectionViewLayout.swift in Sources */,
500D6F181F1E6E5100FE20D4 /* LNZCarouselCollectionViewLayout.swift in Sources */,
500D6EFE1F1E1CFD00FE20D4 /* AppDelegate.swift in Sources */,
501CF9F31FBFABBD003CAC14 /* SafariAnimator.swift in Sources */,
5025EE8F1F42EA6C001521F6 /* LNZSafariTabLayout.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
136 changes: 136 additions & 0 deletions LNZCollectionLayouts/Animator/SafariAnimator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//
// 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) {

}
}
Loading

0 comments on commit eb9cbb8

Please sign in to comment.