Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions LiveCollections.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
Expand Down Expand Up @@ -598,7 +598,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
Expand All @@ -622,7 +622,6 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -650,7 +649,6 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ extension CarouselController: CollectionDataAnimationDelegate {
return .preciseAnimations
}

func animateAlongsideUpdate(with duration: TimeInterval) {
func animateAlongsideUpdate(for state: CollectionDataAnimationState) {
// animate alongside the collection view animation here
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ extension ScenarioSevenViewController: CollectionDataAnimationDelegate {
return .preciseAnimations
}

func animateAlongsideUpdate(with duration: TimeInterval) {
func animateAlongsideUpdate(for state: CollectionDataAnimationState) {
// animate alongside the table view animation here
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ extension ScenarioThreeViewController: CollectionDataAnimationDelegate {
return .preciseAnimations
}

func animateAlongsideUpdate(with duration: TimeInterval) {
func animateAlongsideUpdate(for state: CollectionDataAnimationState) {
// animate alongside the collection view animation here
// use a CollectionDataSynchronizer object if you would like
// all animations to map to a single call
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ extension AnyDeltaUpdatableViewDelegate: DeltaUpdatableViewDelegate {
return animationDelegate?.preferredItemAnimationStyle(for: itemDelta) ?? .preciseAnimations
}

func animateAlongsideUpdate(with duration: TimeInterval) {
animationDelegate?.animateAlongsideUpdate(with: duration)
func animateAlongsideUpdate(for state: CollectionDataAnimationState) {
animationDelegate?.animateAlongsideUpdate(for: state)
}

var view: DeltaUpdatableView? {
Expand Down Expand Up @@ -79,8 +79,8 @@ extension AnySectionDeltaUpdatableViewDelegate: SectionDeltaUpdatableViewDelegat
return animationDelegate?.preferredItemAnimationStyle(for: itemDelta) ?? .preciseAnimations
}

func animateAlongsideUpdate(with duration: TimeInterval) {
animationDelegate?.animateAlongsideUpdate(with: duration)
func animateAlongsideUpdate(for state: CollectionDataAnimationState) {
animationDelegate?.animateAlongsideUpdate(for: state)
}

func preferredSectionAnimationStyle(for sectionDelta: IndexDelta) -> AnimationStyle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,26 @@ public protocol CollectionDataCalculationNotificationDelegate: AnyObject {
func collectionDataDidEndCalculating()
}

// MARK: CollectionDataCalculationNotificationDelegate
// MARK: CollectionDataAnimationDelegate

public enum CollectionDataAnimationGroup {
case insertDeleteMove
case reload
}

public enum CollectionDataAnimationState {
case immediatelyBefore(group: CollectionDataAnimationGroup, duration: TimeInterval)
case immediatelyAfter(group: CollectionDataAnimationGroup, duration: TimeInterval)
case during(group: CollectionDataAnimationGroup, duration: TimeInterval)
case completed(group: CollectionDataAnimationGroup)
}

/**
Use to control the style of animations or to animate alongside a UITableView or UICollectionView animation
*/
public protocol CollectionDataAnimationDelegate: AnyObject {
func preferredItemAnimationStyle(for itemDelta: IndexDelta) -> AnimationStyle
func animateAlongsideUpdate(with duration: TimeInterval)
func animateAlongsideUpdate(for state: CollectionDataAnimationState)
}

// MARK: CollectionSectionDataAnimationDelegate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,25 @@ extension UITableView: DeltaUpdatableView {
}
}

if #available(iOS 11.0, *) {
performBatchUpdates({ [weak self] in
tableViewUpdates.forEach { $0.sectionUpdate.update() }
guard self != nil else { return }
deleteMoveInsert()
tableViewUpdates.uniqueAnimationDelegates.forEach { $0.animateAlongsideUpdate(with: TimeInterval.standardCollectionAnimationDuration) }
}, completion: { [weak self] animationsCompletedSuccessfully in
guard let strongSelf = self else {
performBatchUpdates(.insertDeleteMove, delegates: tableViewUpdates.uniqueAnimationDelegates, { [weak self] in
tableViewUpdates.forEach { $0.sectionUpdate.update() }
guard self != nil else { return }
deleteMoveInsert()
}, completion: { [weak self] animationsCompletedSuccessfully in
guard let strongSelf = self else {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}
guard animationsCompletedSuccessfully else {
strongSelf.reloadData()
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}
strongSelf.performBatchUpdates(.reload, delegates: tableViewUpdates.uniqueAnimationDelegates, { [weak weakSelf = strongSelf] in
guard weakSelf != nil else { return }
reload()
}, completion: { [weak weakSelf = strongSelf] animationsCompletedSuccessfully in
guard let strongSelf = weakSelf else {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}
Expand All @@ -82,73 +93,27 @@ extension UITableView: DeltaUpdatableView {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}
strongSelf.performBatchUpdates({ [weak weakSelf = strongSelf] in
guard weakSelf != nil else { return }
reload()
}, completion: { [weak weakSelf = strongSelf] animationsCompletedSuccessfully in
guard let strongSelf = weakSelf else {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}
guard animationsCompletedSuccessfully else {
strongSelf.reloadData()
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}

tableViewUpdates.manualReload(view: strongSelf) {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
}
})
})
} else {
beginUpdates()
tableViewUpdates.forEach { $0.sectionUpdate.update() }
deleteMoveInsert()
tableViewUpdates.uniqueAnimationDelegates.forEach { $0.animateAlongsideUpdate(with: TimeInterval.standardCollectionAnimationDuration) }
endUpdates()

guard isVisibleOnScreen else {
reloadData()
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
return
}

beginUpdates()
reload()
endUpdates()

tableViewUpdates.manualReload(view: self) {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
}
}
tableViewUpdates.manualReload(view: strongSelf) {
tableViewUpdates.forEach { $0.sectionUpdate.completion?() }
}
})
})
}

public func reloadSections(for sectionUpdates: [SectionUpdate]) {

if #available(iOS 11.0, *) {
performBatchUpdates({ [weak self] in
sectionUpdates.forEach { sectionUpdate in
sectionUpdate.update()
guard self != nil else { return }
let indexSet = IndexSet([sectionUpdate.section])
reloadSections(indexSet, with: preferredReloadSectionAnimation(for: sectionUpdate.section))
}
sectionUpdates.uniqueAnimationDelegates.forEach { $0.animateAlongsideUpdate(with: TimeInterval.standardCollectionAnimationDuration) }
}, completion: { _ in
sectionUpdates.forEach { $0.completion?() }
})
} else {
beginUpdates()
performBatchUpdates({ [weak self] in
sectionUpdates.forEach { sectionUpdate in
sectionUpdate.update()
guard self != nil else { return }
let indexSet = IndexSet([sectionUpdate.section])
reloadSections(indexSet, with: preferredReloadSectionAnimation(for: sectionUpdate.section))
}
sectionUpdates.uniqueAnimationDelegates.forEach { $0.animateAlongsideUpdate(with: TimeInterval.standardCollectionAnimationDuration) }
endUpdates()
}, completion: { _ in
sectionUpdates.forEach { $0.completion?() }
}
})
}
}

Expand Down Expand Up @@ -189,7 +154,7 @@ extension UICollectionView: DeltaUpdatableView {
return
}

performBatchUpdates({ [weak self] in
performBatchUpdates(.insertDeleteMove, delegates: collectionViewUpdates.uniqueAnimationDelegates, { [weak self] in
collectionViewUpdates.forEach { $0.sectionUpdate.update() }
guard let strongSelf = self else { return }
for update in collectionViewUpdates {
Expand All @@ -201,7 +166,6 @@ extension UICollectionView: DeltaUpdatableView {
}
strongSelf.insertItems(at: delta.insertedIndexPaths)
}
collectionViewUpdates.uniqueAnimationDelegates.forEach { $0.animateAlongsideUpdate(with: TimeInterval.standardCollectionAnimationDuration) }
}, completion: { [weak self] animationsCompletedSuccessfully in
guard let strongSelf = self else {
collectionViewUpdates.forEach { $0.sectionUpdate.completion?() }
Expand All @@ -219,7 +183,7 @@ extension UICollectionView: DeltaUpdatableView {
return
}

strongSelf.performBatchUpdates({ [weak weakSelf = strongSelf] in
strongSelf.performBatchUpdates(.reload, delegates: collectionViewUpdates.uniqueAnimationDelegates, { [weak weakSelf = strongSelf] in
guard let strongSelf = weakSelf else { return }
for update in filteredUpdates {
let delta = update.indexPathsToAnimate
Expand Down Expand Up @@ -497,3 +461,39 @@ private extension Sequence where Element == SectionUpdate {
})
}
}

// MARK: AnimationDelegate Helper

private extension UITableView {

func performBatchUpdates(_ group: CollectionDataAnimationGroup, delegates: [CollectionDataAnimationDelegate], _ updates: (() -> Void)?, completion: ((Bool) -> Void)? = nil) {
delegates.forEach { $0.animateAlongsideUpdate(for: .immediatelyBefore(group: group, duration: TimeInterval.standardCollectionAnimationDuration)) }

performBatchUpdates {
updates?()
delegates.forEach { $0.animateAlongsideUpdate(for: .during(group: group, duration: TimeInterval.standardCollectionAnimationDuration)) }
} completion: { value in
completion?(value)
delegates.forEach { $0.animateAlongsideUpdate(for: .completed(group: group)) }
}

delegates.forEach { $0.animateAlongsideUpdate(for: .immediatelyAfter(group: group, duration: TimeInterval.standardCollectionAnimationDuration)) }
}
}

private extension UICollectionView {

func performBatchUpdates(_ group: CollectionDataAnimationGroup, delegates: [CollectionDataAnimationDelegate], _ updates: (() -> Void)?, completion: ((Bool) -> Void)? = nil) {
delegates.forEach { $0.animateAlongsideUpdate(for: .immediatelyBefore(group: group, duration: TimeInterval.standardCollectionAnimationDuration)) }

performBatchUpdates {
updates?()
delegates.forEach { $0.animateAlongsideUpdate(for: .during(group: group, duration: TimeInterval.standardCollectionAnimationDuration)) }
} completion: { value in
completion?(value)
delegates.forEach { $0.animateAlongsideUpdate(for: .completed(group: group)) }
}

delegates.forEach { $0.animateAlongsideUpdate(for: .immediatelyAfter(group: group, duration: TimeInterval.standardCollectionAnimationDuration)) }
}
}
Loading