From 7e515912865a94548520f3db180ddeecf43bcd98 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 23 Mar 2020 15:14:50 -0400 Subject: [PATCH] Refactor to use base classes UIView and UIScrollView Animations can be apply to derived classes such as UICollectionView, etc. --- .../project.pbxproj | 8 +- .../Animations.swift | 104 +++++++++--------- .../Animator.swift | 42 ++++--- ...UITableView+Ext.swift => Extensions.swift} | 9 ++ 4 files changed, 91 insertions(+), 72 deletions(-) rename UITableViewCellAnimation-Article/{UITableView+Ext.swift => Extensions.swift} (61%) diff --git a/UITableViewCellAnimation-Article-Final.xcodeproj/project.pbxproj b/UITableViewCellAnimation-Article-Final.xcodeproj/project.pbxproj index 4cdc3a1..242454f 100644 --- a/UITableViewCellAnimation-Article-Final.xcodeproj/project.pbxproj +++ b/UITableViewCellAnimation-Article-Final.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - D7239181213E7CF900E0D4E5 /* UITableView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7239180213E7CF900E0D4E5 /* UITableView+Ext.swift */; }; + D7239181213E7CF900E0D4E5 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7239180213E7CF900E0D4E5 /* Extensions.swift */; }; D7239187213E986000E0D4E5 /* Animations.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7239186213E986000E0D4E5 /* Animations.swift */; }; D7239189213EA08700E0D4E5 /* Animator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7239188213EA08700E0D4E5 /* Animator.swift */; }; D7C6B695213E6861002A7BB1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7C6B694213E6861002A7BB1 /* AppDelegate.swift */; }; @@ -18,7 +18,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - D7239180213E7CF900E0D4E5 /* UITableView+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Ext.swift"; sourceTree = ""; }; + D7239180213E7CF900E0D4E5 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; D7239186213E986000E0D4E5 /* Animations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Animations.swift; sourceTree = ""; }; D7239188213EA08700E0D4E5 /* Animator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Animator.swift; sourceTree = ""; }; D7C6B691213E6861002A7BB1 /* UITableViewCellAnimation-Article-Final.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "UITableViewCellAnimation-Article-Final.app"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -64,7 +64,7 @@ D7C6B696213E6861002A7BB1 /* TableViewController.swift */, D7239186213E986000E0D4E5 /* Animations.swift */, D7239188213EA08700E0D4E5 /* Animator.swift */, - D7239180213E7CF900E0D4E5 /* UITableView+Ext.swift */, + D7239180213E7CF900E0D4E5 /* Extensions.swift */, D7C6B69B213E6862002A7BB1 /* Assets.xcassets */, D7C6B6A0213E6862002A7BB1 /* Info.plist */, D7C6B69D213E6862002A7BB1 /* LaunchScreen.storyboard */, @@ -147,7 +147,7 @@ D7239189213EA08700E0D4E5 /* Animator.swift in Sources */, D7239187213E986000E0D4E5 /* Animations.swift in Sources */, D7C6B697213E6861002A7BB1 /* TableViewController.swift in Sources */, - D7239181213E7CF900E0D4E5 /* UITableView+Ext.swift in Sources */, + D7239181213E7CF900E0D4E5 /* Extensions.swift in Sources */, D7C6B695213E6861002A7BB1 /* AppDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/UITableViewCellAnimation-Article/Animations.swift b/UITableViewCellAnimation-Article/Animations.swift index f8b8c94..8fb7ee0 100644 --- a/UITableViewCellAnimation-Article/Animations.swift +++ b/UITableViewCellAnimation-Article/Animations.swift @@ -8,66 +8,66 @@ import UIKit -typealias Animation = (UITableViewCell, IndexPath, UITableView) -> Void +typealias Animation = (UIView, IndexPath, UIScrollView) -> Void enum AnimationFactory { - static func makeFade(duration: TimeInterval, delayFactor: Double) -> Animation { - return { cell, indexPath, _ in - cell.alpha = 0 + static func makeFade(duration: TimeInterval, delayFactor: Double) -> Animation { + return { cell, indexPath, _ in + cell.alpha = 0 - UIView.animate( - withDuration: duration, - delay: delayFactor * Double(indexPath.row), - animations: { - cell.alpha = 1 - }) - } - } + UIView.animate( + withDuration: duration, + delay: delayFactor * Double(indexPath.row), + animations: { + cell.alpha = 1 + }) + } + } - static func makeMoveUpWithBounce(rowHeight: CGFloat, duration: TimeInterval, delayFactor: Double) -> Animation { - return { cell, indexPath, tableView in - cell.transform = CGAffineTransform(translationX: 0, y: rowHeight) + static func makeMoveUpWithBounce(rowHeight: CGFloat, duration: TimeInterval, delayFactor: Double) -> Animation { + return { cell, indexPath, scrollView in + cell.transform = CGAffineTransform(translationX: 0, y: rowHeight) - UIView.animate( - withDuration: duration, - delay: delayFactor * Double(indexPath.row), - usingSpringWithDamping: 0.4, - initialSpringVelocity: 0.1, - options: [.curveEaseInOut], - animations: { - cell.transform = CGAffineTransform(translationX: 0, y: 0) - }) - } - } + UIView.animate( + withDuration: duration, + delay: delayFactor * Double(indexPath.row), + usingSpringWithDamping: 0.4, + initialSpringVelocity: 0.1, + options: [.curveEaseInOut], + animations: { + cell.transform = CGAffineTransform(translationX: 0, y: 0) + }) + } + } - static func makeSlideIn(duration: TimeInterval, delayFactor: Double) -> Animation { - return { cell, indexPath, tableView in - cell.transform = CGAffineTransform(translationX: tableView.bounds.width, y: 0) + static func makeSlideIn(duration: TimeInterval, delayFactor: Double) -> Animation { + return { cell, indexPath, scrollView in + cell.transform = CGAffineTransform(translationX: scrollView.bounds.width, y: 0) - UIView.animate( - withDuration: duration, - delay: delayFactor * Double(indexPath.row), - options: [.curveEaseInOut], - animations: { - cell.transform = CGAffineTransform(translationX: 0, y: 0) - }) - } - } + UIView.animate( + withDuration: duration, + delay: delayFactor * Double(indexPath.row), + options: [.curveEaseInOut], + animations: { + cell.transform = CGAffineTransform(translationX: 0, y: 0) + }) + } + } - static func makeMoveUpWithFade(rowHeight: CGFloat, duration: TimeInterval, delayFactor: Double) -> Animation { - return { cell, indexPath, tableView in - cell.transform = CGAffineTransform(translationX: 0, y: rowHeight / 2) - cell.alpha = 0 + static func makeMoveUpWithFade(rowHeight: CGFloat, duration: TimeInterval, delayFactor: Double) -> Animation { + return { cell, indexPath, scrollView in + cell.transform = CGAffineTransform(translationX: 0, y: rowHeight / 2) + cell.alpha = 0 - UIView.animate( - withDuration: duration, - delay: delayFactor * Double(indexPath.row), - options: [.curveEaseInOut], - animations: { - cell.transform = CGAffineTransform(translationX: 0, y: 0) - cell.alpha = 1 - }) - } - } + UIView.animate( + withDuration: duration, + delay: delayFactor * Double(indexPath.row), + options: [.curveEaseInOut], + animations: { + cell.transform = CGAffineTransform(translationX: 0, y: 0) + cell.alpha = 1 + }) + } + } } diff --git a/UITableViewCellAnimation-Article/Animator.swift b/UITableViewCellAnimation-Article/Animator.swift index 97eb431..8a4fe7e 100644 --- a/UITableViewCellAnimation-Article/Animator.swift +++ b/UITableViewCellAnimation-Article/Animator.swift @@ -9,20 +9,30 @@ import UIKit final class Animator { - private var hasAnimatedAllCells = false - private let animation: Animation - - init(animation: @escaping Animation) { - self.animation = animation - } - - func animate(cell: UITableViewCell, at indexPath: IndexPath, in tableView: UITableView) { - guard !hasAnimatedAllCells else { - return - } - - animation(cell, indexPath, tableView) - - hasAnimatedAllCells = tableView.isLastVisibleCell(at: indexPath) - } + private var hasAnimatedAllCells = false + private let animation: Animation + + init(animation: @escaping Animation) { + self.animation = animation + } + + func animate(cell: UIView, at indexPath: IndexPath, in scrollView: UIScrollView) { + guard !hasAnimatedAllCells else { + return + } + + switch(scrollView) { + case is UICollectionView: + animation(cell, indexPath, scrollView as! UICollectionView) + hasAnimatedAllCells = (scrollView as! UICollectionView).isLastVisibleCell(at: indexPath) + break; + case is UITableView: + animation(cell, indexPath, scrollView as! UITableView) + hasAnimatedAllCells = (scrollView as! UITableView).isLastVisibleCell(at: indexPath) + // Add additional handling for UIScrollView derived types of your choice. + default: + break; + } + } + } diff --git a/UITableViewCellAnimation-Article/UITableView+Ext.swift b/UITableViewCellAnimation-Article/Extensions.swift similarity index 61% rename from UITableViewCellAnimation-Article/UITableView+Ext.swift rename to UITableViewCellAnimation-Article/Extensions.swift index b94251f..e8de88a 100644 --- a/UITableViewCellAnimation-Article/UITableView+Ext.swift +++ b/UITableViewCellAnimation-Article/Extensions.swift @@ -17,3 +17,12 @@ extension UITableView { return lastIndexPath == indexPath } } + +extension UICollectionView { + func isLastVisibleCell(at indexPath: IndexPath) -> Bool { + guard let lastIndexPath = indexPathsForVisibleItems.last else { + return false + } + return lastIndexPath == indexPath + } +}