diff --git a/Starbucks.xcodeproj/project.pbxproj b/Starbucks.xcodeproj/project.pbxproj index dd57239..1f65f83 100644 --- a/Starbucks.xcodeproj/project.pbxproj +++ b/Starbucks.xcodeproj/project.pbxproj @@ -17,6 +17,7 @@ 6B1487F923AB97AF0015D2A1 /* CardTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6B1487F823AB97AF0015D2A1 /* CardTableViewCell.swift */; }; D8652DE323ABBB1D00BD2B6F /* CardDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8652DE223ABBB1D00BD2B6F /* CardDetailViewController.swift */; }; D8652DE523ABBE5A00BD2B6F /* String+pdf417Barcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8652DE423ABBE5A00BD2B6F /* String+pdf417Barcode.swift */; }; + D8D6F44323AF6A4C00E0CBE0 /* CardAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D6F44223AF6A4C00E0CBE0 /* CardAnimator.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -32,6 +33,7 @@ 6B1487F823AB97AF0015D2A1 /* CardTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardTableViewCell.swift; sourceTree = ""; }; D8652DE223ABBB1D00BD2B6F /* CardDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardDetailViewController.swift; sourceTree = ""; }; D8652DE423ABBE5A00BD2B6F /* String+pdf417Barcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+pdf417Barcode.swift"; sourceTree = ""; }; + D8D6F44223AF6A4C00E0CBE0 /* CardAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardAnimator.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -71,6 +73,7 @@ D8652DE223ABBB1D00BD2B6F /* CardDetailViewController.swift */, 6B1487F823AB97AF0015D2A1 /* CardTableViewCell.swift */, 6B0F0B8A23AC7BC0000C5AD3 /* GiftView.swift */, + D8D6F44223AF6A4C00E0CBE0 /* CardAnimator.swift */, D8652DE723AF683B00BD2B6F /* Storyboard */, D8652DE623AF680D00BD2B6F /* Extension */, 6B1487ED23AB976B0015D2A1 /* Assets.xcassets */, @@ -168,6 +171,7 @@ files = ( D8652DE323ABBB1D00BD2B6F /* CardDetailViewController.swift in Sources */, D8652DE523ABBE5A00BD2B6F /* String+pdf417Barcode.swift in Sources */, + D8D6F44323AF6A4C00E0CBE0 /* CardAnimator.swift in Sources */, 6B1487E923AB976B0015D2A1 /* CardController.swift in Sources */, 6B1487E523AB976B0015D2A1 /* AppDelegate.swift in Sources */, 6B1487E723AB976B0015D2A1 /* SceneDelegate.swift in Sources */, diff --git a/Starbucks/CardAnimator.swift b/Starbucks/CardAnimator.swift new file mode 100644 index 0000000..a86f37d --- /dev/null +++ b/Starbucks/CardAnimator.swift @@ -0,0 +1,110 @@ +// +// CardAnimator.swift +// Starbucks +// +// Created by Supakit Thanadittagorn on 22/12/19. +// Copyright © 2019 pop. All rights reserved. +// + +import UIKit + +class CardAnimator: NSObject, UIViewControllerAnimatedTransitioning { + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + 3 + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + let containerView = transitionContext.containerView + + guard let fromVC = transitionContext.viewController(forKey: .from) as? CardController, + let toVC = transitionContext.viewController(forKey: .to) as? CardDetailViewController, + let cardView = fromVC.selectedView() else { return } + + cardView.alpha = 0 + + let fromViewSnapshot = fromVC.view.snapshotView(afterScreenUpdates: true)! + let cardBackgroundSnapshot = cardView.backgroundImageView.snapshotView(afterScreenUpdates: false)! + let cardTitleSnapshot = cardView.titleLabel.snapshotView(afterScreenUpdates: false)! + let cardSubTitleSnapshot = cardView.subTitleLabel.snapshotView(afterScreenUpdates: false)! + + cardBackgroundSnapshot.frame = cardView.convert(cardView.backgroundImageView.frame, to: fromVC.view) + cardTitleSnapshot.frame = cardView.convert(cardView.titleLabel.frame, to: fromVC.view) + cardSubTitleSnapshot.frame = cardView.convert(cardView.subTitleLabel.frame, to: fromVC.view) + + containerView.addSubview(fromViewSnapshot) + containerView.addSubview(cardBackgroundSnapshot) + containerView.addSubview(cardTitleSnapshot) + containerView.addSubview(cardSubTitleSnapshot) + + let toView: UIView = toVC.view + toView.layoutIfNeeded() + + let gradientSnapshot = toVC.gradientView.snapshotView(afterScreenUpdates: true)! + let sheetViewSnapshot = toVC.contentBackgroundView.snapshotView(afterScreenUpdates: true)! + sheetViewSnapshot.frame = CGRect( + origin: CGPoint(x: 0, y: fromVC.view.frame.size.height), + size: toVC.contentBackgroundView.frame.size + ) + toView.alpha = 0 + gradientSnapshot.alpha = 0 + containerView.addSubview(toView) + containerView.addSubview(gradientSnapshot) + containerView.addSubview(sheetViewSnapshot) + + UIView.animate(withDuration: 3, animations: { + + cardBackgroundSnapshot.frame = self.frame(for: cardBackgroundSnapshot, scaleTo: toVC.backgroundImageView) + cardTitleSnapshot.frame = self.frame(for: cardTitleSnapshot, usingPositionFrom: toVC.numberLabel) + cardSubTitleSnapshot.frame = self.frame(for: cardSubTitleSnapshot, usingPositionFrom: toVC.amountLabel) + sheetViewSnapshot.frame = toVC.contentBackgroundView.frame + fromViewSnapshot.alpha = 0 + gradientSnapshot.alpha = 1 + }, completion: { _ in + toView.alpha = 1 + cardView.alpha = 1 + fromViewSnapshot.removeFromSuperview() + cardBackgroundSnapshot.removeFromSuperview() + cardTitleSnapshot.removeFromSuperview() + cardSubTitleSnapshot.removeFromSuperview() + sheetViewSnapshot.removeFromSuperview() + gradientSnapshot.removeFromSuperview() + transitionContext.completeTransition(true) + }) + } + + func frame(for l: UIView, usingPositionFrom r: UIView) -> CGRect { + CGRect( + origin: CGPoint(x: r.frame.origin.x, y: r.frame.origin.y + statusBarFrameHeightFromWindow(for: l)), + size: l.frame.size + ) + } + + func frame(for l: UIView, scaleTo r: UIView) -> CGRect { + let h = r.frame.size.width / l.frame.size.width * l.frame.size.height + return CGRect( + origin: CGPoint(x: 0, y: 250 - h), + size: CGSize(width: r.frame.size.width, height: h) + ) + } + + func statusBarFrameHeightFromWindow(for view: UIView) -> CGFloat { + view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0 + } +} + +extension UIView { + /// Replacement of `snapshotView` on iOS 10. Fixes the issue of `snapshotView` returning a blank white screen. + func snapshotImageView() -> UIImageView? { + UIGraphicsBeginImageContext(bounds.size) + guard let context = UIGraphicsGetCurrentContext() else { + return nil + } + + layer.render(in: context) + + let viewImage = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return UIImageView(image: viewImage, highlightedImage: viewImage) + } +} diff --git a/Starbucks/CardController.swift b/Starbucks/CardController.swift index cf098ec..7d5d24a 100644 --- a/Starbucks/CardController.swift +++ b/Starbucks/CardController.swift @@ -23,7 +23,6 @@ class CardController: UIViewController { v.textColor = #colorLiteral(red: 0.03921568627, green: 0.3411764706, blue: 0.2352941176, alpha: 1) return v }() - var tableView = UITableView() var cards = [ @@ -38,6 +37,7 @@ class CardController: UIViewController { image: UIImage(named: "Seattle") ) ] + var selectedIndexPath: IndexPath? override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() @@ -46,6 +46,10 @@ class CardController: UIViewController { } } + var topbarHeight: CGFloat { + return view.window?.windowScene?.statusBarManager?.statusBarFrame.height ?? 0.0 + } + private func setupView() { view.addSubview(screenTitleLabel) view.addSubview(tableView) @@ -66,6 +70,7 @@ class CardController: UIViewController { super.viewDidLoad() setupTableView() navigationController?.navigationBar.isHidden = true + navigationController?.delegate = self } override func viewWillAppear(_ animated: Bool) { @@ -81,6 +86,10 @@ class CardController: UIViewController { tableView.separatorStyle = .none tableView.register(CardTableViewCell.self, forCellReuseIdentifier: "cardCell") } + + func selectedView() -> CardTableViewCell? { + selectedIndexPath.flatMap { tableView.cellForRow(at: $0) as? CardTableViewCell } + } } extension CardController: UITableViewDataSource { @@ -99,9 +108,16 @@ extension CardController: UITableViewDelegate { func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: false) let card = cards[indexPath.row] + selectedIndexPath = indexPath + let detailViewController = CardDetailViewController() detailViewController.card = card navigationController?.pushViewController(detailViewController, animated: true) } } +extension CardController: UINavigationControllerDelegate { + func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { + operation == .push ? CardAnimator() : nil + } +}