From ba75c8f08779b2cf2663c5ff18be65f4e66a9f7c Mon Sep 17 00:00:00 2001 From: Ivan <6350992+bivant@users.noreply.github.com> Date: Sat, 26 Oct 2024 15:01:21 +0300 Subject: [PATCH 1/5] Code style, class -> AnyObject warning fix, upgrade project settings as suggested by xCode 15 --- custom-transitions.xcodeproj/project.pbxproj | 8 ++++++-- .../Transitions/TransitionManager.swift | 12 ++++++------ custom-transitions/View/AlbumDetailHeaderView.swift | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/custom-transitions.xcodeproj/project.pbxproj b/custom-transitions.xcodeproj/project.pbxproj index fcaca10..77ead16 100644 --- a/custom-transitions.xcodeproj/project.pbxproj +++ b/custom-transitions.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -102,6 +102,7 @@ BC78E7A026163BB500371773 /* Products */, ); sourceTree = ""; + usesTabs = 0; }; BC78E7A026163BB500371773 /* Products */ = { isa = PBXGroup; @@ -241,8 +242,9 @@ BC78E79726163BB500371773 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 1230; - LastUpgradeCheck = 1230; + LastUpgradeCheck = 1540; TargetAttributes = { BC78E79E26163BB500371773 = { CreatedOnToolsVersion = 12.3; @@ -370,6 +372,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -431,6 +434,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/custom-transitions/Transitions/TransitionManager.swift b/custom-transitions/Transitions/TransitionManager.swift index 8d34854..a33b78c 100644 --- a/custom-transitions/Transitions/TransitionManager.swift +++ b/custom-transitions/Transitions/TransitionManager.swift @@ -17,7 +17,7 @@ final class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning { } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { - return duration + duration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { @@ -36,10 +36,10 @@ final class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning { // MARK: - UINavigationControllerDelegate extension TransitionManager: UINavigationControllerDelegate { - func navigationController( - _ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, - from fromVC: UIViewController, - to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { + func navigationController(_ navigationController: UINavigationController, + animationControllerFor operation: UINavigationController.Operation, + from fromVC: UIViewController, + to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? { self.operation = operation @@ -84,7 +84,7 @@ private extension TransitionManager { let albumCell = fromViewController.currentCell, let albumCoverImageView = fromViewController.currentCell?.albumCoverImageView, let albumDetailHeaderView = toViewController.headerView - else { return} + else { return } toViewController.view.layoutIfNeeded() diff --git a/custom-transitions/View/AlbumDetailHeaderView.swift b/custom-transitions/View/AlbumDetailHeaderView.swift index c7b90eb..df474af 100644 --- a/custom-transitions/View/AlbumDetailHeaderView.swift +++ b/custom-transitions/View/AlbumDetailHeaderView.swift @@ -7,7 +7,7 @@ import UIKit -protocol AlbumDetailHeaderViewDelegate: class { +protocol AlbumDetailHeaderViewDelegate: AnyObject { func closeButtonDidTap() } From 3b4dcd79a26bd035b3683103cbd7e84a4f3ba92c Mon Sep 17 00:00:00 2001 From: Ivan <6350992+bivant@users.noreply.github.com> Date: Sat, 26 Oct 2024 15:02:07 +0300 Subject: [PATCH 2/5] AlbumsViewController - call super in viewDidAppear --- custom-transitions/ViewController/AlbumsViewController.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom-transitions/ViewController/AlbumsViewController.swift b/custom-transitions/ViewController/AlbumsViewController.swift index faa4d2b..d690ede 100644 --- a/custom-transitions/ViewController/AlbumsViewController.swift +++ b/custom-transitions/ViewController/AlbumsViewController.swift @@ -43,9 +43,8 @@ class AlbumsViewController: UIViewController { } override func viewDidAppear(_ animated: Bool) { - + super.viewDidAppear(animated) } - } // MARK: - Setups From 7b3a117c9ab38cdadc06adae9a9c8771d2cc032b Mon Sep 17 00:00:00 2001 From: Ivan <6350992+bivant@users.noreply.github.com> Date: Sat, 26 Oct 2024 15:05:08 +0300 Subject: [PATCH 3/5] Animate back transition from the AlbumDetailViewController to AlbumsViewController --- .../Transitions/TransitionManager.swift | 55 ++++++++++++++++++- .../ViewController/AlbumsViewController.swift | 22 ++++---- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/custom-transitions/Transitions/TransitionManager.swift b/custom-transitions/Transitions/TransitionManager.swift index a33b78c..e4820bf 100644 --- a/custom-transitions/Transitions/TransitionManager.swift +++ b/custom-transitions/Transitions/TransitionManager.swift @@ -43,7 +43,7 @@ extension TransitionManager: UINavigationControllerDelegate { self.operation = operation - if operation == .push { + if operation == .push || operation == .pop { return self } @@ -71,7 +71,7 @@ private extension TransitionManager { let albumsViewController = toViewController as? AlbumsViewController else { return } - dismissViewController(detailsViewController, to: albumsViewController) + dismissViewController(detailsViewController, to: albumsViewController, with: context) default: break @@ -125,7 +125,56 @@ private extension TransitionManager { animator.startAnimation() } - func dismissViewController(_ fromViewController: AlbumDetailViewController, to toViewController: AlbumsViewController) { + func dismissViewController(_ fromViewController: AlbumDetailViewController, to toViewController: AlbumsViewController, with context: UIViewControllerContextTransitioning) { + guard + let albumCell = toViewController.currentCell, + let albumCoverImageView = toViewController.currentCell?.albumCoverImageView, + let albumDetailHeaderView = fromViewController.headerView, + let albumDetailRootView = fromViewController.view + else { return } + + toViewController.view.layoutIfNeeded() + + let containerView = context.containerView + + let backgroundFillView = UIView() + backgroundFillView.frame = fromViewController.view.frame + AlbumsViewController.setupBackgroundColor(for: backgroundFillView) + + let snapshotContentView = UIView() + snapshotContentView.backgroundColor = albumDetailRootView.backgroundColor + snapshotContentView.frame = containerView.convert(albumDetailHeaderView.frame, from: albumDetailRootView) + snapshotContentView.layer.cornerRadius = albumCell.contentView.layer.cornerRadius + + let snapshotAlbumCoverImageView = UIImageView() + snapshotAlbumCoverImageView.clipsToBounds = true + snapshotAlbumCoverImageView.contentMode = albumDetailHeaderView.contentMode + snapshotAlbumCoverImageView.image = albumDetailHeaderView.albumCoverImageView.image + snapshotAlbumCoverImageView.layer.cornerRadius = albumDetailHeaderView.layer.cornerRadius + snapshotAlbumCoverImageView.frame = containerView.convert(albumDetailHeaderView.albumCoverImageView.frame, from: albumDetailHeaderView) + + containerView.addSubview(backgroundFillView) + containerView.addSubview(toViewController.view) + containerView.addSubview(snapshotContentView) + containerView.addSubview(snapshotAlbumCoverImageView) + + toViewController.view.isHidden = true + let animator = UIViewPropertyAnimator(duration: duration, curve: .easeInOut) { + snapshotContentView.frame = containerView.convert(albumCell.contentView.frame, from: albumCell) + snapshotContentView.backgroundColor = albumCell.contentView.backgroundColor + snapshotAlbumCoverImageView.frame = containerView.convert(albumCoverImageView.frame, from: albumCell) + snapshotAlbumCoverImageView.layer.cornerRadius = albumCoverImageView.layer.cornerRadius + } + + animator.addCompletion { position in + toViewController.view.isHidden = false + backgroundFillView.removeFromSuperview() + snapshotAlbumCoverImageView.removeFromSuperview() + snapshotContentView.removeFromSuperview() + context.completeTransition(position == .end) + } + + animator.startAnimation() } } diff --git a/custom-transitions/ViewController/AlbumsViewController.swift b/custom-transitions/ViewController/AlbumsViewController.swift index d690ede..3d7e95a 100644 --- a/custom-transitions/ViewController/AlbumsViewController.swift +++ b/custom-transitions/ViewController/AlbumsViewController.swift @@ -45,6 +45,16 @@ class AlbumsViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } + + static func setupBackgroundColor(for view: UIView) { + let gradient = CAGradientLayer() + gradient.frame = view.bounds + gradient.colors = [UIColor.backgroundColorGradient1 ?? .black, UIColor.backgroundColorGradient2 ?? .black].map { $0.cgColor } + gradient.startPoint = .init(x: 0.5, y: 0) + gradient.endPoint = .init(x: 0.5, y: 1) + gradient.locations = [0.5] + view.layer.insertSublayer(gradient, at: 0) + } } // MARK: - Setups @@ -52,22 +62,12 @@ class AlbumsViewController: UIViewController { private extension AlbumsViewController { func setupUI() { - setupBackgroundColor() + Self.setupBackgroundColor(for: view) setupHeaderView() setupCollectionView() setupPageControl() } - func setupBackgroundColor() { - let gradient = CAGradientLayer() - gradient.frame = view.bounds - gradient.colors = [UIColor.backgroundColorGradient1 ?? .black, UIColor.backgroundColorGradient2 ?? .black].map { $0.cgColor } - gradient.startPoint = .init(x: 0.5, y: 0) - gradient.endPoint = .init(x: 0.5, y: 1) - gradient.locations = [0.5] - view.layer.insertSublayer(gradient, at: 0) - } - func setupHeaderView() { let bandNameLabel = UILabel() bandNameLabel.font = .titleFont From 7f437a7b1c455f641d3e3da105c0fb7a88ad53d8 Mon Sep 17 00:00:00 2001 From: Ivan <6350992+bivant@users.noreply.github.com> Date: Sat, 26 Oct 2024 15:12:23 +0300 Subject: [PATCH 4/5] Use snapshots as background for the back navigation --- .../Transitions/TransitionManager.swift | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/custom-transitions/Transitions/TransitionManager.swift b/custom-transitions/Transitions/TransitionManager.swift index e4820bf..2078ab8 100644 --- a/custom-transitions/Transitions/TransitionManager.swift +++ b/custom-transitions/Transitions/TransitionManager.swift @@ -11,6 +11,7 @@ final class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning { private let duration: TimeInterval private var operation = UINavigationController.Operation.push + private var popSnapshots = [UIViewController: UIView]() init(duration: TimeInterval) { self.duration = duration @@ -63,6 +64,8 @@ private extension TransitionManager { let detailsViewController = toViewController as? AlbumDetailViewController else { return } + popSnapshots[albumsViewController] = fromViewController.view.snapshotView(afterScreenUpdates: false) + presentViewController(detailsViewController, from: albumsViewController, with: context) case .pop: @@ -137,13 +140,20 @@ private extension TransitionManager { let containerView = context.containerView - let backgroundFillView = UIView() - backgroundFillView.frame = fromViewController.view.frame - AlbumsViewController.setupBackgroundColor(for: backgroundFillView) + let backgroundFillView: UIView + if let snapshot = popSnapshots[toViewController] { + backgroundFillView = snapshot + backgroundFillView.frame = fromViewController.view.frame + popSnapshots[toViewController] = nil + } else { + backgroundFillView = UIView() + backgroundFillView.frame = fromViewController.view.frame + AlbumsViewController.setupBackgroundColor(for: backgroundFillView) + } let snapshotContentView = UIView() snapshotContentView.backgroundColor = albumDetailRootView.backgroundColor - snapshotContentView.frame = containerView.convert(albumDetailHeaderView.frame, from: albumDetailRootView) + snapshotContentView.frame = albumDetailRootView.frame snapshotContentView.layer.cornerRadius = albumCell.contentView.layer.cornerRadius let snapshotAlbumCoverImageView = UIImageView() From 557632aae3f785ec2d8337bec8cacdbf38087609 Mon Sep 17 00:00:00 2001 From: Ivan <6350992+bivant@users.noreply.github.com> Date: Sun, 27 Oct 2024 23:28:42 +0300 Subject: [PATCH 5/5] Remove extra backgroundFillView for the pop animation as can use the AlbumsViewController.view directly --- .../Transitions/TransitionManager.swift | 19 ---------------- .../ViewController/AlbumsViewController.swift | 22 +++++++++---------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/custom-transitions/Transitions/TransitionManager.swift b/custom-transitions/Transitions/TransitionManager.swift index 2078ab8..6c3cbed 100644 --- a/custom-transitions/Transitions/TransitionManager.swift +++ b/custom-transitions/Transitions/TransitionManager.swift @@ -11,7 +11,6 @@ final class TransitionManager: NSObject, UIViewControllerAnimatedTransitioning { private let duration: TimeInterval private var operation = UINavigationController.Operation.push - private var popSnapshots = [UIViewController: UIView]() init(duration: TimeInterval) { self.duration = duration @@ -64,8 +63,6 @@ private extension TransitionManager { let detailsViewController = toViewController as? AlbumDetailViewController else { return } - popSnapshots[albumsViewController] = fromViewController.view.snapshotView(afterScreenUpdates: false) - presentViewController(detailsViewController, from: albumsViewController, with: context) case .pop: @@ -140,17 +137,6 @@ private extension TransitionManager { let containerView = context.containerView - let backgroundFillView: UIView - if let snapshot = popSnapshots[toViewController] { - backgroundFillView = snapshot - backgroundFillView.frame = fromViewController.view.frame - popSnapshots[toViewController] = nil - } else { - backgroundFillView = UIView() - backgroundFillView.frame = fromViewController.view.frame - AlbumsViewController.setupBackgroundColor(for: backgroundFillView) - } - let snapshotContentView = UIView() snapshotContentView.backgroundColor = albumDetailRootView.backgroundColor snapshotContentView.frame = albumDetailRootView.frame @@ -163,13 +149,10 @@ private extension TransitionManager { snapshotAlbumCoverImageView.layer.cornerRadius = albumDetailHeaderView.layer.cornerRadius snapshotAlbumCoverImageView.frame = containerView.convert(albumDetailHeaderView.albumCoverImageView.frame, from: albumDetailHeaderView) - containerView.addSubview(backgroundFillView) containerView.addSubview(toViewController.view) containerView.addSubview(snapshotContentView) containerView.addSubview(snapshotAlbumCoverImageView) - toViewController.view.isHidden = true - let animator = UIViewPropertyAnimator(duration: duration, curve: .easeInOut) { snapshotContentView.frame = containerView.convert(albumCell.contentView.frame, from: albumCell) snapshotContentView.backgroundColor = albumCell.contentView.backgroundColor @@ -178,8 +161,6 @@ private extension TransitionManager { } animator.addCompletion { position in - toViewController.view.isHidden = false - backgroundFillView.removeFromSuperview() snapshotAlbumCoverImageView.removeFromSuperview() snapshotContentView.removeFromSuperview() context.completeTransition(position == .end) diff --git a/custom-transitions/ViewController/AlbumsViewController.swift b/custom-transitions/ViewController/AlbumsViewController.swift index 3d7e95a..d690ede 100644 --- a/custom-transitions/ViewController/AlbumsViewController.swift +++ b/custom-transitions/ViewController/AlbumsViewController.swift @@ -45,16 +45,6 @@ class AlbumsViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) } - - static func setupBackgroundColor(for view: UIView) { - let gradient = CAGradientLayer() - gradient.frame = view.bounds - gradient.colors = [UIColor.backgroundColorGradient1 ?? .black, UIColor.backgroundColorGradient2 ?? .black].map { $0.cgColor } - gradient.startPoint = .init(x: 0.5, y: 0) - gradient.endPoint = .init(x: 0.5, y: 1) - gradient.locations = [0.5] - view.layer.insertSublayer(gradient, at: 0) - } } // MARK: - Setups @@ -62,12 +52,22 @@ class AlbumsViewController: UIViewController { private extension AlbumsViewController { func setupUI() { - Self.setupBackgroundColor(for: view) + setupBackgroundColor() setupHeaderView() setupCollectionView() setupPageControl() } + func setupBackgroundColor() { + let gradient = CAGradientLayer() + gradient.frame = view.bounds + gradient.colors = [UIColor.backgroundColorGradient1 ?? .black, UIColor.backgroundColorGradient2 ?? .black].map { $0.cgColor } + gradient.startPoint = .init(x: 0.5, y: 0) + gradient.endPoint = .init(x: 0.5, y: 1) + gradient.locations = [0.5] + view.layer.insertSublayer(gradient, at: 0) + } + func setupHeaderView() { let bandNameLabel = UILabel() bandNameLabel.font = .titleFont