Implement ASChangeSetDataController.

This commit is contained in:
Huy Nguyen
2015-10-19 22:08:34 +03:00
parent 7a97c5ca00
commit 5ea4b1b92d
10 changed files with 648 additions and 5 deletions

View File

@@ -237,6 +237,14 @@
9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9CDC18CC1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9CDC18CD1B910E12004965E2 /* ASLayoutablePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; }; 9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; };
AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */; };
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */; };
AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */; };
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */; };
AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */; };
AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */; };
AC026B581BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */; }; AC026B581BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */; };
AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; }; AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -597,6 +605,10 @@
9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = "<group>"; }; 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASStackBaselinePositionedLayout.mm; sourceTree = "<group>"; };
9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = "<group>"; }; 9CDC18CB1B910E12004965E2 /* ASLayoutablePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutablePrivate.h; path = AsyncDisplayKit/Layout/ASLayoutablePrivate.h; sourceTree = "<group>"; };
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = "<group>"; }; 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = "<group>"; };
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASChangeSetDataController.h; sourceTree = "<group>"; };
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASChangeSetDataController.m; sourceTree = "<group>"; };
AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASHierarchyChangeSet.h; sourceTree = "<group>"; };
AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _ASHierarchyChangeSet.m; sourceTree = "<group>"; };
AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASStaticLayoutSpecSnapshotTests.m; sourceTree = "<group>"; }; AC026B571BD3F61800BBC17E /* ASStaticLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASStaticLayoutSpecSnapshotTests.m; sourceTree = "<group>"; };
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = "<group>"; }; AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = "<group>"; };
AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = "<group>"; }; AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = "<group>"; };
@@ -890,6 +902,8 @@
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */, 205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */,
464052191A3F83C40061C0BA /* ASDataController.h */, 464052191A3F83C40061C0BA /* ASDataController.h */,
4640521A1A3F83C40061C0BA /* ASDataController.mm */, 4640521A1A3F83C40061C0BA /* ASDataController.mm */,
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */,
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */,
05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */, 05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */,
05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */, 05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */,
4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */, 4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */,
@@ -954,6 +968,8 @@
058D0A01195D050800B7D73C /* Private */ = { 058D0A01195D050800B7D73C /* Private */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */,
AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */,
9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */, 9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */,
9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */, 9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */,
9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */, 9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */,
@@ -1075,6 +1091,7 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
058D0A71195D05F800B7D73C /* _AS-objc-internal.h in Headers */, 058D0A71195D05F800B7D73C /* _AS-objc-internal.h in Headers */,
058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */, 058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */,
058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */, 058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */,
@@ -1133,6 +1150,7 @@
292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */, 292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */,
ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */, ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */,
ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */, ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */,
AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
0516FA3D1A15563400B4EBED /* ASLog.h in Headers */, 0516FA3D1A15563400B4EBED /* ASLog.h in Headers */,
0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */, 0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */, 0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */,
@@ -1182,6 +1200,7 @@
isa = PBXHeadersBuildPhase; isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */, B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */,
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */, B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
@@ -1202,6 +1221,7 @@
B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */, B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */,
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */, B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */, 044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */,
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */, B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */, 34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */, 18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
@@ -1478,6 +1498,7 @@
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */, 058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */, 058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */,
058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */, 058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */,
AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */,
058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */, 058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */,
058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */, 058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */,
9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */, 9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */,
@@ -1533,6 +1554,7 @@
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */, ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */,
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */, ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */, 055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */, 058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */, 058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */,
@@ -1593,6 +1615,7 @@
9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */, 9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */,
B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */, B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */,
B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */, B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */,
AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */,
B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */, B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */,
B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */, B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */,
2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */, 2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */,
@@ -1648,6 +1671,7 @@
34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */, 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */,
34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */, 34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */,
34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */, 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */,
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */, 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */,
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */, B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */, B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */,

View File

@@ -9,7 +9,7 @@
#import "ASTableView.h" #import "ASTableView.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASDataController.h" #import "ASChangeSetDataController.h"
#import "ASCollectionViewLayoutController.h" #import "ASCollectionViewLayoutController.h"
#import "ASLayoutController.h" #import "ASLayoutController.h"
#import "ASRangeController.h" #import "ASRangeController.h"
@@ -210,7 +210,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
_rangeController.layoutController = _layoutController; _rangeController.layoutController = _layoutController;
_rangeController.delegate = self; _rangeController.delegate = self;
_dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled]; _dataController = [[ASChangeSetDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled];
_dataController.dataSource = self; _dataController.dataSource = self;
_dataController.delegate = _rangeController; _dataController.delegate = _rangeController;

View File

@@ -0,0 +1,19 @@
//
// ASChangeSetDataController.h
// AsyncDisplayKit
//
// Created by Huy Nguyen on 19/10/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import <AsyncDisplayKit/ASDataController.h>
/**
* Subclass of ASDataController that enqueues and sorts edit commands during batch updating (using _ASHierarchyChangeSet).
*
* @see ASDataController
* @see _ASHierarchyChangeSet
*/
@interface ASChangeSetDataController : ASDataController
@end

View File

@@ -0,0 +1,179 @@
//
// ASChangeSetDataController.m
// AsyncDisplayKit
//
// Created by Huy Nguyen on 19/10/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "ASChangeSetDataController.h"
#import "ASInternalHelpers.h"
#import "_ASHierarchyChangeSet.h"
#import "ASAssert.h"
@interface ASChangeSetDataController ()
@property (nonatomic, assign) NSUInteger batchUpdateCounter;
@property (nonatomic, strong) _ASHierarchyChangeSet *changeSet;
@end
@implementation ASChangeSetDataController
- (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
{
if (!(self = [super initWithAsyncDataFetching:asyncDataFetchingEnabled])) {
return nil;
}
_batchUpdateCounter = 0;
return self;
}
#pragma mark - Batching (External API)
- (void)beginUpdates
{
ASDisplayNodeAssertMainThread();
if (_batchUpdateCounter == 0) {
_changeSet = [_ASHierarchyChangeSet new];
}
_batchUpdateCounter++;
}
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
{
ASDisplayNodeAssertMainThread();
_batchUpdateCounter--;
if (_batchUpdateCounter == 0) {
[_changeSet markCompleted];
[super beginUpdates];
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeReload]) {
[super reloadSections:change.indexSet withAnimationOptions:change.animationOptions];
}
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeReload]) {
[super reloadRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
}
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) {
[super deleteRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
}
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) {
[super deleteSections:change.indexSet withAnimationOptions:change.animationOptions];
}
for (_ASHierarchySectionChange *change in [_changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) {
[super insertSections:change.indexSet withAnimationOptions:change.animationOptions];
}
for (_ASHierarchyItemChange *change in [_changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) {
[super insertRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
}
[super endUpdatesAnimated:animated completion:completion];
_changeSet = nil;
}
}
- (BOOL)batchUpdating
{
BOOL batchUpdating = (_batchUpdateCounter != 0);
// _changeSet must be available during batch update
ASDisplayNodeAssertTrue(batchUpdating == (_changeSet != nil));
return batchUpdating;
}
#pragma mark - Section Editing (External API)
- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet insertSections:sections animationOptions:animationOptions];
} else {
[super insertSections:sections withAnimationOptions:animationOptions];
}
}
- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet deleteSections:sections animationOptions:animationOptions];
} else {
[super deleteSections:sections withAnimationOptions:animationOptions];
}
}
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet reloadSections:sections animationOptions:animationOptions];
} else {
[super reloadSections:sections withAnimationOptions:animationOptions];
}
}
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet deleteSections:[NSIndexSet indexSetWithIndex:section] animationOptions:animationOptions];
[_changeSet insertSections:[NSIndexSet indexSetWithIndex:newSection] animationOptions:animationOptions];
} else {
[super moveSection:section toSection:newSection withAnimationOptions:animationOptions];
}
}
#pragma mark - Row Editing (External API)
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet insertItems:indexPaths animationOptions:animationOptions];
} else {
[super insertRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
}
}
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet deleteItems:indexPaths animationOptions:animationOptions];
} else {
[super deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
}
}
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet reloadItems:indexPaths animationOptions:animationOptions];
} else {
[super reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animationOptions];
}
}
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
{
ASDisplayNodeAssertMainThread();
if ([self batchUpdating]) {
[_changeSet deleteItems:@[indexPath] animationOptions:animationOptions];
[_changeSet insertItems:@[newIndexPath] animationOptions:animationOptions];
} else {
[super moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:animationOptions];
}
}
@end

View File

@@ -8,7 +8,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDataController.h> #import <AsyncDisplayKit/ASChangeSetDataController.h>
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimension.h>
@class ASDisplayNode; @class ASDisplayNode;
@@ -32,7 +32,7 @@
@end @end
@interface ASCollectionDataController : ASDataController @interface ASCollectionDataController : ASChangeSetDataController
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath; - (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;

View File

@@ -97,7 +97,7 @@ typedef NSUInteger ASDataControllerAnimationOptions;
* *
* All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the data * All operations are asynchronous and thread safe. You can call it from background thread (it is recommendated) and the data
* will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called. * will be updated asynchronously. The dataSource must be updated to reflect the changes before these methods has been called.
* For each data updatin, the corresponding methods in delegate will be called. * For each data updating, the corresponding methods in delegate will be called.
*/ */
@protocol ASFlowLayoutControllerDataSource; @protocol ASFlowLayoutControllerDataSource;
@interface ASDataController : ASDealloc2MainObject <ASFlowLayoutControllerDataSource> @interface ASDataController : ASDealloc2MainObject <ASFlowLayoutControllerDataSource>

View File

@@ -26,3 +26,7 @@ CGFloat ASCeilPixelValue(CGFloat f);
CGFloat ASRoundPixelValue(CGFloat f); CGFloat ASRoundPixelValue(CGFloat f);
ASDISPLAYNODE_EXTERN_C_END ASDISPLAYNODE_EXTERN_C_END
@interface NSIndexPath (ASInverseComparison)
- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath;
@end

View File

@@ -70,3 +70,12 @@ CGFloat ASRoundPixelValue(CGFloat f)
{ {
return roundf(f * ASScreenScale()) / ASScreenScale(); return roundf(f * ASScreenScale()) / ASScreenScale();
} }
@implementation NSIndexPath (ASInverseComparison)
- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath
{
return [otherIndexPath compare:self];
}
@end

View File

@@ -0,0 +1,72 @@
//
// _ASHierarchyChangeSet.h
// AsyncDisplayKit
//
// Created by Adlai Holler on 9/29/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "ASDataController.h"
typedef NS_ENUM(NSInteger, _ASHierarchyChangeType) {
_ASHierarchyChangeTypeReload,
_ASHierarchyChangeTypeDelete,
_ASHierarchyChangeTypeInsert
};
@interface _ASHierarchySectionChange : NSObject
// FIXME: Generalize this to `changeMetadata` dict?
@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions;
@property (nonatomic, strong, readonly) NSIndexSet *indexSet;
@property (nonatomic, readonly) _ASHierarchyChangeType changeType;
@end
@interface _ASHierarchyItemChange : NSObject
@property (nonatomic, readonly) ASDataControllerAnimationOptions animationOptions;
/// Index paths are sorted descending for changeType .Delete, ascending otherwise
@property (nonatomic, strong, readonly) NSArray *indexPaths;
@property (nonatomic, readonly) _ASHierarchyChangeType changeType;
@end
@interface _ASHierarchyChangeSet : NSObject
@property (nonatomic, strong, readonly) NSIndexSet *deletedSections;
@property (nonatomic, strong, readonly) NSIndexSet *insertedSections;
@property (nonatomic, strong, readonly) NSIndexSet *reloadedSections;
@property (nonatomic, strong, readonly) NSArray *insertedItems;
@property (nonatomic, strong, readonly) NSArray *deletedItems;
@property (nonatomic, strong, readonly) NSArray *reloadedItems;
@property (nonatomic, readonly) BOOL completed;
/// Call this once the change set has been constructed to prevent future modifications to the changeset. Calling this more than once is a programmer error.
- (void)markCompleted;
/**
@abstract Return sorted changes of the given type, grouped by animation options.
Items deleted from deleted sections are not reported.
Items inserted into inserted sections are not reported.
Items reloaded in reloaded sections are not reported.
The safe order for processing change groups is:
- Reloaded sections & reloaded items
- Deleted items, descending order
- Deleted sections, descending order
- Inserted sections, ascending order
- Inserted items, ascending order
*/
- (NSArray /*<_ASHierarchySectionChange *>*/ *)sectionChangesOfType:(_ASHierarchyChangeType)changeType;
- (NSArray /*<_ASHierarchyItemChange *>*/ *)itemChangesOfType:(_ASHierarchyChangeType)changeType;
- (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options;
- (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options;
- (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options;
- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options;
- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options;
- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options;
@end

View File

@@ -0,0 +1,336 @@
//
// _ASHierarchyChangeSet.m
// AsyncDisplayKit
//
// Created by Adlai Holler on 9/29/15.
// Copyright © 2015 Facebook. All rights reserved.
//
#import "_ASHierarchyChangeSet.h"
#import "ASInternalHelpers.h"
@interface _ASHierarchySectionChange ()
- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions;
/**
On return `changes` is sorted according to the change type with changes coalesced by animationOptions
Assumes: `changes` is [_ASHierarchySectionChange] all with the same changeType
*/
+ (void)sortAndCoalesceChanges:(NSMutableArray *)changes;
@end
@interface _ASHierarchyItemChange ()
- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions presorted:(BOOL)presorted;
/**
On return `changes` is sorted according to the change type with changes coalesced by animationOptions
Assumes: `changes` is [_ASHierarchyItemChange] all with the same changeType
*/
+ (void)sortAndCoalesceChanges:(NSMutableArray *)changes ignoringChangesInSections:(NSIndexSet *)sections;
@end
@interface _ASHierarchyChangeSet ()
@property (nonatomic, strong, readonly) NSMutableArray *insertItemChanges;
@property (nonatomic, strong, readonly) NSMutableArray *deleteItemChanges;
@property (nonatomic, strong, readonly) NSMutableArray *reloadItemChanges;
@property (nonatomic, strong, readonly) NSMutableArray *insertSectionChanges;
@property (nonatomic, strong, readonly) NSMutableArray *deleteSectionChanges;
@property (nonatomic, strong, readonly) NSMutableArray *reloadSectionChanges;
@end
@implementation _ASHierarchyChangeSet {
NSMutableIndexSet *_deletedSections;
NSMutableIndexSet *_insertedSections;
NSMutableIndexSet *_reloadedSections;
NSMutableArray *_insertedItems;
NSMutableArray *_deletedItems;
NSMutableArray *_reloadedItems;
}
- (instancetype)init
{
self = [super init];
if (self) {
_deletedSections = [NSMutableIndexSet new];
_insertedSections = [NSMutableIndexSet new];
_reloadedSections = [NSMutableIndexSet new];
_deletedItems = [NSMutableArray new];
_insertedItems = [NSMutableArray new];
_reloadedItems = [NSMutableArray new];
_insertItemChanges = [NSMutableArray new];
_deleteItemChanges = [NSMutableArray new];
_reloadItemChanges = [NSMutableArray new];
_insertSectionChanges = [NSMutableArray new];
_deleteSectionChanges = [NSMutableArray new];
_reloadSectionChanges = [NSMutableArray new];
}
return self;
}
#pragma mark External API
- (void)markCompleted
{
NSAssert(!_completed, @"Attempt to mark already-completed changeset as completed.");
_completed = YES;
[self _sortAndCoalesceChangeArrays];
}
- (NSArray *)sectionChangesOfType:(_ASHierarchyChangeType)changeType
{
[self _ensureCompleted];
switch (changeType) {
case _ASHierarchyChangeTypeInsert:
return _insertSectionChanges;
case _ASHierarchyChangeTypeReload:
return _reloadSectionChanges;
case _ASHierarchyChangeTypeDelete:
return _deleteSectionChanges;
default:
NSAssert(NO, @"Request for section changes with invalid type: %lu", changeType);
}
}
- (NSArray *)itemChangesOfType:(_ASHierarchyChangeType)changeType
{
[self _ensureCompleted];
switch (changeType) {
case _ASHierarchyChangeTypeInsert:
return _insertItemChanges;
case _ASHierarchyChangeTypeReload:
return _reloadItemChanges;
case _ASHierarchyChangeTypeDelete:
return _deleteItemChanges;
default:
NSAssert(NO, @"Request for item changes with invalid type: %lu", changeType);
}
}
- (void)deleteItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options
{
[self _ensureNotCompleted];
_ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexPaths:indexPaths animationOptions:options presorted:NO];
[_deleteItemChanges addObject:change];
}
- (void)deleteSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options
{
[self _ensureNotCompleted];
_ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeDelete indexSet:sections animationOptions:options];
[_deleteSectionChanges addObject:change];
}
- (void)insertItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options
{
[self _ensureNotCompleted];
_ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexPaths:indexPaths animationOptions:options presorted:NO];
[_insertItemChanges addObject:change];
}
- (void)insertSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options
{
[self _ensureNotCompleted];
_ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeInsert indexSet:sections animationOptions:options];
[_insertSectionChanges addObject:change];
}
- (void)reloadItems:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options
{
[self _ensureNotCompleted];
_ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:_ASHierarchyChangeTypeReload indexPaths:indexPaths animationOptions:options presorted:NO];
[_reloadItemChanges addObject:change];
}
- (void)reloadSections:(NSIndexSet *)sections animationOptions:(ASDataControllerAnimationOptions)options
{
[self _ensureNotCompleted];
_ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:_ASHierarchyChangeTypeReload indexSet:sections animationOptions:options];
[_reloadSectionChanges addObject:change];
}
#pragma mark Private
- (BOOL)_ensureNotCompleted
{
NSAssert(!_completed, @"Attempt to modify completed changeset %@", self);
return !_completed;
}
- (BOOL)_ensureCompleted
{
NSAssert(_completed, @"Attempt to process incomplete changeset %@", self);
return _completed;
}
- (void)_sortAndCoalesceChangeArrays
{
@autoreleasepool {
[_ASHierarchySectionChange sortAndCoalesceChanges:_deleteSectionChanges];
[_ASHierarchySectionChange sortAndCoalesceChanges:_insertSectionChanges];
[_ASHierarchySectionChange sortAndCoalesceChanges:_reloadSectionChanges];
[_ASHierarchyItemChange sortAndCoalesceChanges:_deleteItemChanges ignoringChangesInSections:_deletedSections];
[_ASHierarchyItemChange sortAndCoalesceChanges:_reloadItemChanges ignoringChangesInSections:_reloadedSections];
[_ASHierarchyItemChange sortAndCoalesceChanges:_insertItemChanges ignoringChangesInSections:_insertedSections];
}
}
@end
@implementation _ASHierarchySectionChange
- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexSet:(NSIndexSet *)indexSet animationOptions:(ASDataControllerAnimationOptions)animationOptions
{
self = [super init];
if (self) {
_changeType = changeType;
_indexSet = indexSet;
_animationOptions = animationOptions;
}
return self;
}
+ (void)sortAndCoalesceChanges:(NSMutableArray *)changes
{
if (changes.count < 1) {
return;
}
_ASHierarchyChangeType type = [changes.firstObject changeType];
// Lookup table [Int: AnimationOptions]
NSMutableDictionary *animationOptions = [NSMutableDictionary new];
// All changed indexes, sorted
NSMutableIndexSet *allIndexes = [NSMutableIndexSet new];
for (_ASHierarchySectionChange *change in changes) {
[change.indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, __unused BOOL *stop) {
animationOptions[@(idx)] = @(change.animationOptions);
}];
[allIndexes addIndexes:change.indexSet];
}
// Create new changes by grouping sorted changes by animation option
NSMutableArray *result = [NSMutableArray new];
__block ASDataControllerAnimationOptions currentOptions = 0;
__block NSMutableIndexSet *currentIndexes = nil;
NSEnumerationOptions options = type == _ASHierarchyChangeTypeDelete ? NSEnumerationReverse : kNilOptions;
[allIndexes enumerateIndexesWithOptions:options usingBlock:^(NSUInteger idx, __unused BOOL * stop) {
ASDataControllerAnimationOptions options = [animationOptions[@(idx)] integerValue];
if (currentIndexes == nil) {
// Starting a new group
currentIndexes = [NSMutableIndexSet indexSetWithIndex:idx];
currentOptions = options;
} else if (options == currentOptions) {
// Continuing the current group
[currentIndexes addIndex:idx];
} else {
// Ending the current group
_ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:currentIndexes animationOptions:currentOptions];
[result addObject:change];
currentOptions = 0;
currentIndexes = nil;
}
}];
if (currentIndexes != nil) {
// Ending the last group
_ASHierarchySectionChange *change = [[_ASHierarchySectionChange alloc] initWithChangeType:type indexSet:currentIndexes animationOptions:currentOptions];
[result addObject:change];
currentOptions = 0;
currentIndexes = nil;
}
[changes setArray:result];
}
@end
@implementation _ASHierarchyItemChange
- (instancetype)initWithChangeType:(_ASHierarchyChangeType)changeType indexPaths:(NSArray *)indexPaths animationOptions:(ASDataControllerAnimationOptions)animationOptions presorted:(BOOL)presorted
{
self = [super init];
if (self) {
_changeType = changeType;
if (presorted) {
_indexPaths = indexPaths;
} else {
SEL sorting = changeType == _ASHierarchyChangeTypeDelete ? @selector(as_inverseCompare:) : @selector(compare:);
_indexPaths = [indexPaths sortedArrayUsingSelector:sorting];
}
_animationOptions = animationOptions;
}
return self;
}
+ (void)sortAndCoalesceChanges:(NSMutableArray *)changes ignoringChangesInSections:(NSIndexSet *)sections
{
if (changes.count < 1) {
return;
}
_ASHierarchyChangeType type = [changes.firstObject changeType];
// Lookup table [NSIndexPath: AnimationOptions]
NSMutableDictionary *animationOptions = [NSMutableDictionary new];
// All changed index paths, sorted
NSMutableArray *allIndexPaths = [NSMutableArray new];
NSPredicate *indexPathInValidSection = [NSPredicate predicateWithBlock:^BOOL(NSIndexPath *indexPath, __unused NSDictionary *_) {
return ![sections containsIndex:indexPath.section];
}];
for (_ASHierarchyItemChange *change in changes) {
for (NSIndexPath *indexPath in change.indexPaths) {
if ([indexPathInValidSection evaluateWithObject:indexPath]) {
animationOptions[indexPath] = @(change.animationOptions);
[allIndexPaths addObject:indexPath];
}
}
}
SEL sorting = type == _ASHierarchyChangeTypeDelete ? @selector(as_inverseCompare:) : @selector(compare:);
[allIndexPaths sortUsingSelector:sorting];
// Create new changes by grouping sorted changes by animation option
NSMutableArray *result = [NSMutableArray new];
ASDataControllerAnimationOptions currentOptions = 0;
NSMutableArray *currentIndexPaths = nil;
for (NSIndexPath *indexPath in allIndexPaths) {
ASDataControllerAnimationOptions options = [animationOptions[indexPath] integerValue];
if (currentIndexPaths == nil) {
// Starting a new group
currentIndexPaths = [NSMutableArray arrayWithObject:indexPath];
currentOptions = options;
} else if (options == currentOptions) {
// Continuing the current group
[currentIndexPaths addObject:indexPath];
} else {
// Ending the current group
_ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:currentIndexPaths animationOptions:currentOptions presorted:YES];
[result addObject:change];
currentOptions = 0;
currentIndexPaths = nil;
}
}
if (currentIndexPaths != nil) {
// Ending the last group
_ASHierarchyItemChange *change = [[_ASHierarchyItemChange alloc] initWithChangeType:type indexPaths:currentIndexPaths animationOptions:currentOptions presorted:YES];
[result addObject:change];
currentOptions = 0;
currentIndexPaths = nil;
}
[changes setArray:result];
}
@end