mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
[ASDataController] Simplify data controller (#2923)
* Start removing ASChangeSetDataController * Continue removing ASChangeSetDataController * Remove unnecessary change * ASDataController is no longer an abstract class, remove its assertion * Get back beginUpdates and endUpdatesAnimated:completion in ASCollectionNode
This commit is contained in:
@@ -321,9 +321,6 @@
|
||||
A2763D7A1CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h in Headers */ = {isa = PBXBuildFile; fileRef = A2763D771CBDD57D00A9ADBD /* ASPINRemoteImageDownloader.h */; };
|
||||
A37320101C571B740011FC94 /* ASTextNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = A373200E1C571B050011FC94 /* ASTextNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AC026B581BD3F61800BBC17E /* ASAbsoluteLayoutSpecSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC026B571BD3F61800BBC17E /* ASAbsoluteLayoutSpecSnapshotTests.m */; };
|
||||
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.mm */; };
|
||||
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.mm */; };
|
||||
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */; };
|
||||
AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm */; };
|
||||
AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm */; };
|
||||
@@ -578,7 +575,6 @@
|
||||
F7CE6C4E1D2CDB3E00BE4C15 /* ASThread.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 058D0A12195D050800B7D73C /* ASThread.h */; };
|
||||
F7CE6C4F1D2CDB3E00BE4C15 /* CoreGraphics+ASConvenience.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 205F0E1F1B376416007741D0 /* CoreGraphics+ASConvenience.h */; };
|
||||
F7CE6C501D2CDB3E00BE4C15 /* ASDataController.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 464052191A3F83C40061C0BA /* ASDataController.h */; };
|
||||
F7CE6C511D2CDB3E00BE4C15 /* ASChangeSetDataController.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */; };
|
||||
F7CE6C521D2CDB3E00BE4C15 /* ASIndexedNodeContext.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = E5711A2A1C840C81009619D4 /* ASIndexedNodeContext.h */; };
|
||||
F7CE6C531D2CDB3E00BE4C15 /* NSMutableAttributedString+TextKitAdditions.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 058D09F5195D050800B7D73C /* NSMutableAttributedString+TextKitAdditions.h */; };
|
||||
F7CE6C541D2CDB3E00BE4C15 /* _ASAsyncTransaction.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 058D09F8195D050800B7D73C /* _ASAsyncTransaction.h */; };
|
||||
@@ -757,7 +753,6 @@
|
||||
F7CE6C4E1D2CDB3E00BE4C15 /* ASThread.h in CopyFiles */,
|
||||
F7CE6C4F1D2CDB3E00BE4C15 /* CoreGraphics+ASConvenience.h in CopyFiles */,
|
||||
F7CE6C501D2CDB3E00BE4C15 /* ASDataController.h in CopyFiles */,
|
||||
F7CE6C511D2CDB3E00BE4C15 /* ASChangeSetDataController.h in CopyFiles */,
|
||||
F7CE6C521D2CDB3E00BE4C15 /* ASIndexedNodeContext.h in CopyFiles */,
|
||||
F7CE6C531D2CDB3E00BE4C15 /* NSMutableAttributedString+TextKitAdditions.h in CopyFiles */,
|
||||
F7CE6C541D2CDB3E00BE4C15 /* _ASAsyncTransaction.h in CopyFiles */,
|
||||
@@ -1088,8 +1083,6 @@
|
||||
A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASTextKitFontSizeAdjuster.h; path = TextKit/ASTextKitFontSizeAdjuster.h; sourceTree = "<group>"; };
|
||||
A373200E1C571B050011FC94 /* ASTextNode+Beta.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASTextNode+Beta.h"; sourceTree = "<group>"; };
|
||||
AC026B571BD3F61800BBC17E /* ASAbsoluteLayoutSpecSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASAbsoluteLayoutSpecSnapshotTests.m; sourceTree = "<group>"; };
|
||||
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASChangeSetDataController.h; sourceTree = "<group>"; };
|
||||
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASChangeSetDataController.mm; sourceTree = "<group>"; };
|
||||
AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _ASHierarchyChangeSet.h; sourceTree = "<group>"; };
|
||||
AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _ASHierarchyChangeSet.mm; sourceTree = "<group>"; };
|
||||
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = "<group>"; };
|
||||
@@ -1695,8 +1688,6 @@
|
||||
251B8EF31BBB3D690087C538 /* ASCollectionDataController.mm */,
|
||||
464052191A3F83C40061C0BA /* ASDataController.h */,
|
||||
4640521A1A3F83C40061C0BA /* ASDataController.mm */,
|
||||
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */,
|
||||
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.mm */,
|
||||
E5711A2A1C840C81009619D4 /* ASIndexedNodeContext.h */,
|
||||
E5711A2D1C840C96009619D4 /* ASIndexedNodeContext.mm */,
|
||||
AC6145401D8AFAE8003D62A2 /* ASSection.h */,
|
||||
@@ -1834,7 +1825,6 @@
|
||||
69E0E8A71D356C9400627613 /* ASEqualityHelpers.h in Headers */,
|
||||
698C8B621CAB49FC0052DC3F /* ASLayoutElementExtensibility.h in Headers */,
|
||||
698548641CA9E025008A345F /* ASEnvironment.h in Headers */,
|
||||
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
|
||||
69F10C871C84C35D0026140C /* ASRangeControllerUpdateRangeProtocol+Beta.h in Headers */,
|
||||
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
|
||||
9C70F20D1CDBE9CB007D6C76 /* ASDefaultPlayButton.h in Headers */,
|
||||
@@ -2312,7 +2302,6 @@
|
||||
81EE38501C8E94F000456208 /* ASRunLoopQueue.mm in Sources */,
|
||||
9C70F2041CDA4EFA007D6C76 /* ASTraitCollection.m in Sources */,
|
||||
ACF6ED321B17843500DA7C62 /* ASAbsoluteLayoutSpec.mm in Sources */,
|
||||
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */,
|
||||
690C35611E055C5D00069B91 /* ASDimensionInternal.mm in Sources */,
|
||||
68355B311CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */,
|
||||
68C215591DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */,
|
||||
@@ -2503,7 +2492,6 @@
|
||||
83A7D95B1D44547700BF333E /* ASWeakMap.m in Sources */,
|
||||
DE84918E1C8FFF9F003D89E9 /* ASRunLoopQueue.mm in Sources */,
|
||||
68FC85E51CE29B7E00EDD713 /* ASTabBarController.m in Sources */,
|
||||
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.mm in Sources */,
|
||||
34EFC7741B701D0A00AD841F /* ASAbsoluteLayoutSpec.mm in Sources */,
|
||||
690C35621E055C5D00069B91 /* ASDimensionInternal.mm in Sources */,
|
||||
68C2155A1DE10D330019C4BC /* ASCollectionViewLayoutInspector.m in Sources */,
|
||||
|
||||
@@ -529,7 +529,7 @@
|
||||
|
||||
- (void)beginUpdates
|
||||
{
|
||||
[self.dataController beginUpdates];
|
||||
[self.view beginUpdates];
|
||||
}
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated
|
||||
@@ -539,7 +539,7 @@
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||
{
|
||||
[self.dataController endUpdatesAnimated:animated completion:completion];
|
||||
[self.view endUpdatesAnimated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)sections
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#import "ASCollectionViewLayoutFacilitatorProtocol.h"
|
||||
#import "ASSectionContext.h"
|
||||
#import "ASCollectionView+Undeprecated.h"
|
||||
#import "_ASHierarchyChangeSet.h"
|
||||
|
||||
/**
|
||||
* A macro to get self.collectionNode and assign it to a local variable, or return
|
||||
@@ -196,7 +197,17 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
* (0 sections) we always check at least once after each update (initial reload is the first update.)
|
||||
*/
|
||||
BOOL _hasEverCheckedForBatchFetchingDueToUpdate;
|
||||
|
||||
|
||||
/**
|
||||
* The change set that we're currently building, if any.
|
||||
*/
|
||||
_ASHierarchyChangeSet *_changeSet;
|
||||
|
||||
/**
|
||||
* Counter used to keep track of nested batch updates.
|
||||
*/
|
||||
NSInteger _batchUpdateCount;
|
||||
|
||||
struct {
|
||||
unsigned int scrollViewDidScroll:1;
|
||||
unsigned int scrollViewWillBeginDragging:1;
|
||||
@@ -352,6 +363,8 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
- (void)dealloc
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeCAssert(_batchUpdateCount == 0, @"ASCollectionView deallocated in the middle of a batch update.");
|
||||
|
||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation, due to animation completion blocks etc.
|
||||
_isDeallocating = YES;
|
||||
[self setAsyncDelegate:nil];
|
||||
@@ -402,6 +415,12 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
- (void)waitUntilAllUpdatesAreCommitted
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (_batchUpdateCount > 0) {
|
||||
// This assertion will be enabled soon.
|
||||
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
|
||||
return;
|
||||
}
|
||||
|
||||
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||
}
|
||||
|
||||
@@ -761,15 +780,43 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
return _dataController;
|
||||
}
|
||||
|
||||
- (void)beginUpdates
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
// _changeSet must be available during batch update
|
||||
ASDisplayNodeAssertTrue((_batchUpdateCount > 0) == (_changeSet != nil));
|
||||
|
||||
if (_batchUpdateCount == 0) {
|
||||
_changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[_dataController itemCountsFromDataSource]];
|
||||
}
|
||||
_batchUpdateCount++;
|
||||
}
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssertNotNil(_changeSet, @"_changeSet must be available when batch update ends");
|
||||
|
||||
_batchUpdateCount--;
|
||||
// Prevent calling endUpdatesAnimated:completion: in an unbalanced way
|
||||
NSAssert(_batchUpdateCount >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call");
|
||||
|
||||
[_changeSet addCompletionHandler:completion];
|
||||
|
||||
if (_batchUpdateCount == 0) {
|
||||
[_dataController updateWithChangeSet:_changeSet animated:animated];
|
||||
_changeSet = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performBatchAnimated:(BOOL)animated updates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
[_dataController beginUpdates];
|
||||
[self beginUpdates];
|
||||
if (updates) {
|
||||
updates();
|
||||
}
|
||||
[_dataController endUpdatesAnimated:animated completion:completion];
|
||||
[self endUpdatesAnimated:animated completion:completion];
|
||||
}
|
||||
|
||||
- (void)performBatchUpdates:(void (^)())updates completion:(void (^)(BOOL))completion
|
||||
@@ -789,27 +836,35 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (sections.count == 0) { return; }
|
||||
[_dataController insertSections:sections withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet insertSections:sections animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)sections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (sections.count == 0) { return; }
|
||||
[_dataController deleteSections:sections withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet deleteSections:sections animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (sections.count == 0) { return; }
|
||||
[_dataController reloadSections:sections withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet reloadSections:sections animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[_dataController moveSection:section toSection:newSection withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet moveSection:section toSection:newSection animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (id<ASSectionContext>)contextForSection:(NSInteger)section
|
||||
@@ -822,27 +877,35 @@ static NSString * const kReuseIdentifier = @"_ASCollectionReuseIdentifier";
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (indexPaths.count == 0) { return; }
|
||||
[_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet insertItems:indexPaths animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)deleteItemsAtIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (indexPaths.count == 0) { return; }
|
||||
[_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet deleteItems:indexPaths animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (indexPaths.count == 0) { return; }
|
||||
[_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet reloadItems:indexPaths animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:kASCollectionViewAnimationNone];
|
||||
[self performBatchUpdates:^{
|
||||
[_changeSet moveItemAtIndexPath:indexPath toIndexPath:newIndexPath animationOptions:kASCollectionViewAnimationNone];
|
||||
} completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@@ -10,18 +10,18 @@
|
||||
|
||||
#import "ASTableViewInternal.h"
|
||||
|
||||
#import "_ASCoreAnimationExtras.h"
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "_ASHierarchyChangeSet.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASAvailability.h"
|
||||
#import "ASBatchFetching.h"
|
||||
#import "ASCellNode+Internal.h"
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASDelegateProxy.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "_ASCoreAnimationExtras.h"
|
||||
#import "ASTableNode.h"
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
@@ -154,6 +154,16 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
// This is useful because we need to measure our row nodes against (width - indexView.width).
|
||||
__weak UIView *_sectionIndexView;
|
||||
|
||||
/**
|
||||
* The change set that we're currently building, if any.
|
||||
*/
|
||||
_ASHierarchyChangeSet *_changeSet;
|
||||
|
||||
/**
|
||||
* Counter used to keep track of nested batch updates.
|
||||
*/
|
||||
NSInteger _batchUpdateCount;
|
||||
|
||||
struct {
|
||||
unsigned int scrollViewDidScroll:1;
|
||||
unsigned int scrollViewWillBeginDragging:1;
|
||||
@@ -230,7 +240,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
|
||||
+ (Class)dataControllerClass
|
||||
{
|
||||
return [ASChangeSetDataController class];
|
||||
return [ASDataController class];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -303,6 +313,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
- (void)dealloc
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeCAssert(_batchUpdateCount == 0, @"ASTableView deallocated in the middle of a batch update.");
|
||||
|
||||
// Sometimes the UIKit classes can call back to their delegate even during deallocation.
|
||||
_isDeallocating = YES;
|
||||
[self setAsyncDelegate:nil];
|
||||
@@ -605,7 +617,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
NSIndexPath *indexPath = [_dataController completedIndexPathForNode:cellNode];
|
||||
indexPath = [self validateIndexPath:indexPath];
|
||||
if (indexPath == nil && wait) {
|
||||
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||
[self waitUntilAllUpdatesAreCommitted];
|
||||
indexPath = [_dataController completedIndexPathForNode:cellNode];
|
||||
indexPath = [self validateIndexPath:indexPath];
|
||||
}
|
||||
@@ -631,7 +643,13 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
- (void)beginUpdates
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[_dataController beginUpdates];
|
||||
// _changeSet must be available during batch update
|
||||
ASDisplayNodeAssertTrue((_batchUpdateCount > 0) == (_changeSet != nil));
|
||||
|
||||
if (_batchUpdateCount == 0) {
|
||||
_changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[_dataController itemCountsFromDataSource]];
|
||||
}
|
||||
_batchUpdateCount++;
|
||||
}
|
||||
|
||||
- (void)endUpdates
|
||||
@@ -643,12 +661,29 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL completed))completion;
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[_dataController endUpdatesAnimated:animated completion:completion];
|
||||
ASDisplayNodeAssertNotNil(_changeSet, @"_changeSet must be available when batch update ends");
|
||||
|
||||
_batchUpdateCount--;
|
||||
// Prevent calling endUpdatesAnimated:completion: in an unbalanced way
|
||||
NSAssert(_batchUpdateCount >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call");
|
||||
|
||||
[_changeSet addCompletionHandler:completion];
|
||||
|
||||
if (_batchUpdateCount == 0) {
|
||||
[_dataController updateWithChangeSet:_changeSet animated:animated];
|
||||
_changeSet = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)waitUntilAllUpdatesAreCommitted
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (_batchUpdateCount > 0) {
|
||||
// This assertion will be enabled soon.
|
||||
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
|
||||
return;
|
||||
}
|
||||
|
||||
[_dataController waitUntilAllUpdatesAreCommitted];
|
||||
}
|
||||
|
||||
@@ -681,54 +716,70 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (sections.count == 0) { return; }
|
||||
[_dataController insertSections:sections withAnimationOptions:animation];
|
||||
[self beginUpdates];
|
||||
[_changeSet insertSections:sections animationOptions:animation];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (sections.count == 0) { return; }
|
||||
[_dataController deleteSections:sections withAnimationOptions:animation];
|
||||
[self beginUpdates];
|
||||
[_changeSet deleteSections:sections animationOptions:animation];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (sections.count == 0) { return; }
|
||||
[_dataController reloadSections:sections withAnimationOptions:animation];
|
||||
[self beginUpdates];
|
||||
[_changeSet reloadSections:sections animationOptions:animation];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[_dataController moveSection:section toSection:newSection withAnimationOptions:UITableViewRowAnimationNone];
|
||||
[self beginUpdates];
|
||||
[_changeSet moveSection:section toSection:newSection animationOptions:UITableViewRowAnimationNone];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (indexPaths.count == 0) { return; }
|
||||
[_dataController insertRowsAtIndexPaths:indexPaths withAnimationOptions:animation];
|
||||
[self beginUpdates];
|
||||
[_changeSet insertItems:indexPaths animationOptions:animation];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (indexPaths.count == 0) { return; }
|
||||
[_dataController deleteRowsAtIndexPaths:indexPaths withAnimationOptions:animation];
|
||||
[self beginUpdates];
|
||||
[_changeSet deleteItems:indexPaths animationOptions:animation];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (indexPaths.count == 0) { return; }
|
||||
[_dataController reloadRowsAtIndexPaths:indexPaths withAnimationOptions:animation];
|
||||
[self beginUpdates];
|
||||
[_changeSet reloadItems:indexPaths animationOptions:animation];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[_dataController moveRowAtIndexPath:indexPath toIndexPath:newIndexPath withAnimationOptions:UITableViewRowAnimationNone];
|
||||
[self beginUpdates];
|
||||
[_changeSet moveItemAtIndexPath:indexPath toIndexPath:newIndexPath animationOptions:UITableViewRowAnimationNone];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#import <AsyncDisplayKit/ASTabBarController.h>
|
||||
#import <AsyncDisplayKit/ASRangeControllerUpdateRangeProtocol+Beta.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASChangeSetDataController.h>
|
||||
#import <AsyncDisplayKit/ASDataController.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
//
|
||||
// ASChangeSetDataController.h
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Huy Nguyen on 19/10/15.
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASDataController.h>
|
||||
|
||||
/**
|
||||
* @abstract Subclass of ASDataController that simulates ordering of operations in batch updates defined in UITableView and UICollectionView.
|
||||
*
|
||||
* @discussion The ordering is achieved by using _ASHierarchyChangeSet to enqueue and sort operations.
|
||||
* More information about the ordering and the index paths used for operations can be found here:
|
||||
* https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html#//apple_ref/doc/uid/TP40007451-CH10-SW17
|
||||
*
|
||||
* @see ASDataController
|
||||
* @see _ASHierarchyChangeSet
|
||||
*/
|
||||
@interface ASChangeSetDataController : ASDataController
|
||||
|
||||
@end
|
||||
@@ -1,208 +0,0 @@
|
||||
//
|
||||
// ASChangeSetDataController.m
|
||||
// AsyncDisplayKit
|
||||
//
|
||||
// Created by Huy Nguyen on 19/10/15.
|
||||
//
|
||||
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "_ASHierarchyChangeSet.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASDataController+Subclasses.h"
|
||||
|
||||
@implementation ASChangeSetDataController {
|
||||
NSInteger _changeSetBatchUpdateCounter;
|
||||
_ASHierarchyChangeSet *_changeSet;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
ASDisplayNodeCAssert(_changeSetBatchUpdateCounter == 0, @"ASChangeSetDataController deallocated in the middle of a batch update.");
|
||||
}
|
||||
|
||||
#pragma mark - Batching (External API)
|
||||
|
||||
- (void)beginUpdates
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (_changeSetBatchUpdateCounter <= 0) {
|
||||
_changeSetBatchUpdateCounter = 0;
|
||||
_changeSet = [[_ASHierarchyChangeSet alloc] initWithOldData:[self itemCountsFromDataSource]];
|
||||
}
|
||||
_changeSetBatchUpdateCounter++;
|
||||
}
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
_changeSetBatchUpdateCounter--;
|
||||
|
||||
// Prevent calling endUpdatesAnimated:completion: in an unbalanced way
|
||||
NSAssert(_changeSetBatchUpdateCounter >= 0, @"endUpdatesAnimated:completion: called without having a balanced beginUpdates call");
|
||||
|
||||
[_changeSet addCompletionHandler:completion];
|
||||
if (_changeSetBatchUpdateCounter == 0) {
|
||||
void (^batchCompletion)(BOOL) = _changeSet.completionHandler;
|
||||
|
||||
/**
|
||||
* If the initial reloadData has not been called, just bail because we don't have
|
||||
* our old data source counts.
|
||||
* See ASUICollectionViewTests.testThatIssuingAnUpdateBeforeInitialReloadIsUnacceptable
|
||||
* For the issue that UICollectionView has that we're choosing to workaround.
|
||||
*/
|
||||
if (!self.initialReloadDataHasBeenCalled) {
|
||||
if (batchCompletion != nil) {
|
||||
batchCompletion(YES);
|
||||
}
|
||||
_changeSet = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidateDataSourceItemCounts];
|
||||
|
||||
// Attempt to mark the update completed. This is when update validation will occur inside the changeset.
|
||||
// If an invalid update exception is thrown, we catch it and inject our "validationErrorSource" object,
|
||||
// which is the table/collection node's data source, into the exception reason to help debugging.
|
||||
@try {
|
||||
[_changeSet markCompletedWithNewItemCounts:[self itemCountsFromDataSource]];
|
||||
} @catch (NSException *e) {
|
||||
id responsibleDataSource = self.validationErrorSource;
|
||||
if (e.name == ASCollectionInvalidUpdateException && responsibleDataSource != nil) {
|
||||
[NSException raise:ASCollectionInvalidUpdateException format:@"%@: %@", [responsibleDataSource class], e.reason];
|
||||
} else {
|
||||
@throw e;
|
||||
}
|
||||
}
|
||||
|
||||
ASDataControllerLogEvent(self, @"triggeredUpdate: %@", _changeSet);
|
||||
|
||||
[super beginUpdates];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
#if ASEVENTLOG_ENABLE
|
||||
NSString *changeSetDescription = ASObjectDescriptionMakeTiny(_changeSet);
|
||||
batchCompletion = ^(BOOL finished) {
|
||||
if (batchCompletion != nil) {
|
||||
batchCompletion(finished);
|
||||
}
|
||||
ASDataControllerLogEvent(self, @"finishedUpdate: %@", changeSetDescription);
|
||||
};
|
||||
#endif
|
||||
|
||||
[super endUpdatesAnimated:animated completion:batchCompletion];
|
||||
|
||||
_changeSet = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)batchUpdating
|
||||
{
|
||||
BOOL batchUpdating = (_changeSetBatchUpdateCounter != 0);
|
||||
// _changeSet must be available during batch update
|
||||
ASDisplayNodeAssertTrue(batchUpdating == (_changeSet != nil));
|
||||
return batchUpdating;
|
||||
}
|
||||
|
||||
- (void)waitUntilAllUpdatesAreCommitted
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if (self.batchUpdating) {
|
||||
// This assertion will be enabled soon.
|
||||
// ASDisplayNodeFailAssert(@"Should not call %@ during batch update", NSStringFromSelector(_cmd));
|
||||
return;
|
||||
}
|
||||
|
||||
[super waitUntilAllUpdatesAreCommitted];
|
||||
}
|
||||
|
||||
#pragma mark - Section Editing (External API)
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet insertSections:sections animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet deleteSections:sections animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet reloadSections:sections animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet deleteSections:[NSIndexSet indexSetWithIndex:section] animationOptions:animationOptions];
|
||||
[_changeSet insertSections:[NSIndexSet indexSetWithIndex:newSection] animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
#pragma mark - Row Editing (External API)
|
||||
|
||||
- (void)insertRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet insertItems:indexPaths animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet deleteItems:indexPaths animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet reloadItems:indexPaths animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
[self beginUpdates];
|
||||
[_changeSet deleteItems:@[indexPath] animationOptions:animationOptions];
|
||||
[_changeSet insertItems:@[newIndexPath] animationOptions:animationOptions];
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASChangeSetDataController.h>
|
||||
#import <AsyncDisplayKit/ASDataController.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
|
||||
@class ASDisplayNode;
|
||||
@@ -41,7 +41,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionDataController : ASChangeSetDataController
|
||||
@interface ASCollectionDataController : ASDataController
|
||||
|
||||
- (instancetype)initWithDataSource:(id<ASCollectionDataControllerSource>)dataSource eventLog:(nullable ASEventLog *)eventLog NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (nullable NSArray<NSIndexPath *> *)convertIndexPathsToCollectionNode:(nullable NSArray<NSIndexPath *> *)indexPaths;
|
||||
|
||||
- (void)beginUpdates;
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(nullable void (^)(BOOL))completion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASFlowLayoutController.h>
|
||||
#import <AsyncDisplayKit/ASEventLog.h>
|
||||
#ifdef __cplusplus
|
||||
#import <vector>
|
||||
#endif
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@@ -25,6 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ASCellNode;
|
||||
@class ASDataController;
|
||||
@class _ASHierarchyChangeSet;
|
||||
@protocol ASEnvironment;
|
||||
|
||||
typedef NSUInteger ASDataControllerAnimationOptions;
|
||||
@@ -137,6 +141,16 @@ extern NSString * const ASCollectionInvalidUpdateException;
|
||||
*/
|
||||
@property (nonatomic, weak) id<ASDataControllerEnvironmentDelegate> environmentDelegate;
|
||||
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
* Returns the most recently gathered item counts from the data source. If the counts
|
||||
* have been invalidated, this synchronously queries the data source and saves the result.
|
||||
*
|
||||
* This must be called on the main thread.
|
||||
*/
|
||||
- (std::vector<NSInteger>)itemCountsFromDataSource;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns YES if reloadData has been called at least once. Before this point it is
|
||||
* important to ignore/suppress some operations. For example, inserting a section
|
||||
@@ -155,25 +169,7 @@ extern NSString * const ASCollectionInvalidUpdateException;
|
||||
|
||||
/** @name Data Updating */
|
||||
|
||||
- (void)beginUpdates;
|
||||
|
||||
- (void)endUpdates;
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^ _Nullable)(BOOL))completion;
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)deleteSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)deleteRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet animated:(BOOL)animated;
|
||||
|
||||
/**
|
||||
* Re-measures all loaded nodes in the backing store.
|
||||
@@ -183,8 +179,6 @@ extern NSString * const ASCollectionInvalidUpdateException;
|
||||
*/
|
||||
- (void)relayoutAllNodes;
|
||||
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
- (void)reloadDataWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions completion:(void (^ _Nullable)())completion;
|
||||
|
||||
- (void)reloadDataImmediatelyWithAnimationOptions:(ASDataControllerAnimationOptions)animationOptions;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#import "ASDataController.h"
|
||||
|
||||
#import "_ASHierarchyChangeSet.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASCellNode.h"
|
||||
#import "ASEnvironmentInternal.h"
|
||||
@@ -77,7 +78,6 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
ASDisplayNodeAssert(![self isMemberOfClass:[ASDataController class]], @"ASDataController is an abstract class and should not be instantiated. Instantiate a subclass instead.");
|
||||
|
||||
_dataSource = dataSource;
|
||||
|
||||
@@ -580,11 +580,6 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
|
||||
});
|
||||
}
|
||||
|
||||
- (void)endUpdates
|
||||
{
|
||||
[self endUpdatesAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
|
||||
{
|
||||
LOG(@"endUpdatesWithCompletion - beginning");
|
||||
@@ -603,6 +598,74 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
|
||||
});
|
||||
}
|
||||
|
||||
- (void)updateWithChangeSet:(_ASHierarchyChangeSet *)changeSet animated:(BOOL)animated
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
void (^batchCompletion)(BOOL) = changeSet.completionHandler;
|
||||
|
||||
/**
|
||||
* If the initial reloadData has not been called, just bail because we don't have
|
||||
* our old data source counts.
|
||||
* See ASUICollectionViewTests.testThatIssuingAnUpdateBeforeInitialReloadIsUnacceptable
|
||||
* For the issue that UICollectionView has that we're choosing to workaround.
|
||||
*/
|
||||
if (!self.initialReloadDataHasBeenCalled) {
|
||||
if (batchCompletion != nil) {
|
||||
batchCompletion(YES);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidateDataSourceItemCounts];
|
||||
|
||||
// Attempt to mark the update completed. This is when update validation will occur inside the changeset.
|
||||
// If an invalid update exception is thrown, we catch it and inject our "validationErrorSource" object,
|
||||
// which is the table/collection node's data source, into the exception reason to help debugging.
|
||||
@try {
|
||||
[changeSet markCompletedWithNewItemCounts:[self itemCountsFromDataSource]];
|
||||
} @catch (NSException *e) {
|
||||
id responsibleDataSource = self.validationErrorSource;
|
||||
if (e.name == ASCollectionInvalidUpdateException && responsibleDataSource != nil) {
|
||||
[NSException raise:ASCollectionInvalidUpdateException format:@"%@: %@", [responsibleDataSource class], e.reason];
|
||||
} else {
|
||||
@throw e;
|
||||
}
|
||||
}
|
||||
|
||||
ASDataControllerLogEvent(self, @"triggeredUpdate: %@", changeSet);
|
||||
|
||||
[self beginUpdates];
|
||||
|
||||
for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeDelete]) {
|
||||
[self deleteRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
|
||||
}
|
||||
|
||||
for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeDelete]) {
|
||||
[self deleteSections:change.indexSet withAnimationOptions:change.animationOptions];
|
||||
}
|
||||
|
||||
for (_ASHierarchySectionChange *change in [changeSet sectionChangesOfType:_ASHierarchyChangeTypeInsert]) {
|
||||
[self insertSections:change.indexSet withAnimationOptions:change.animationOptions];
|
||||
}
|
||||
|
||||
for (_ASHierarchyItemChange *change in [changeSet itemChangesOfType:_ASHierarchyChangeTypeInsert]) {
|
||||
[self insertRowsAtIndexPaths:change.indexPaths withAnimationOptions:change.animationOptions];
|
||||
}
|
||||
|
||||
#if ASEVENTLOG_ENABLE
|
||||
NSString *changeSetDescription = ASObjectDescriptionMakeTiny(changeSet);
|
||||
batchCompletion = ^(BOOL finished) {
|
||||
if (batchCompletion != nil) {
|
||||
batchCompletion(finished);
|
||||
}
|
||||
ASDataControllerLogEvent(self, @"finishedUpdate: %@", changeSetDescription);
|
||||
};
|
||||
#endif
|
||||
|
||||
[self endUpdatesAnimated:animated completion:batchCompletion];
|
||||
}
|
||||
|
||||
#pragma mark - Section Editing (External API)
|
||||
|
||||
- (void)insertSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
@@ -665,17 +728,6 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
|
||||
});
|
||||
}
|
||||
|
||||
- (void)reloadSections:(NSIndexSet *)sections withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController the reload will be broken into delete & insert.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController and the move will be processed along with the current batch of updates.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Backing store manipulation optional hooks (Subclass API)
|
||||
|
||||
- (void)prepareForReloadDataWithSectionCount:(NSInteger)newSectionCount
|
||||
@@ -794,11 +846,6 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
|
||||
});
|
||||
}
|
||||
|
||||
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController and the reload will be broken into delete & insert.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
- (void)relayoutAllNodes
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
@@ -843,11 +890,6 @@ NSString * const ASCollectionInvalidUpdateException = @"ASCollectionInvalidUpdat
|
||||
}
|
||||
}
|
||||
|
||||
- (void)moveRowAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath withAnimationOptions:(ASDataControllerAnimationOptions)animationOptions
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"ASDataController does not support %@. Call this on ASChangeSetDataController and the move will be processed along with the current batch of updates.", NSStringFromSelector(_cmd));
|
||||
}
|
||||
|
||||
#pragma mark - Data Querying (Subclass API)
|
||||
|
||||
- (NSArray<NSIndexPath *> *)indexPathsForEditingNodesOfKind:(NSString *)kind
|
||||
|
||||
@@ -34,21 +34,6 @@ typedef void (^ASDataControllerCompletionBlock)(NSArray<ASCellNode *> *nodes, NS
|
||||
*/
|
||||
- (NSMutableArray *)completedNodesOfKind:(NSString *)kind;
|
||||
|
||||
/**
|
||||
* Ensure that next time `itemCountsFromDataSource` is called, new values are retrieved.
|
||||
*
|
||||
* This must be called on the main thread.
|
||||
*/
|
||||
- (void)invalidateDataSourceItemCounts;
|
||||
|
||||
/**
|
||||
* Returns the most recently gathered item counts from the data source. If the counts
|
||||
* have been invalidated, this synchronously queries the data source and saves the result.
|
||||
*
|
||||
* This must be called on the main thread.
|
||||
*/
|
||||
- (std::vector<NSInteger>)itemCountsFromDataSource;
|
||||
|
||||
#pragma mark - Node sizing
|
||||
|
||||
/**
|
||||
|
||||
@@ -145,6 +145,9 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType);
|
||||
- (void)insertItems:(NSArray<NSIndexPath *> *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options;
|
||||
- (void)deleteItems:(NSArray<NSIndexPath *> *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options;
|
||||
- (void)reloadItems:(NSArray<NSIndexPath *> *)indexPaths animationOptions:(ASDataControllerAnimationOptions)options;
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection animationOptions:(ASDataControllerAnimationOptions)options;
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath animationOptions:(ASDataControllerAnimationOptions)options;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -287,6 +287,24 @@ NSString *NSStringFromASHierarchyChangeType(_ASHierarchyChangeType changeType)
|
||||
[_reloadSectionChanges addObject:change];
|
||||
}
|
||||
|
||||
- (void)moveItemAtIndexPath:(NSIndexPath *)indexPath toIndexPath:(NSIndexPath *)newIndexPath animationOptions:(ASDataControllerAnimationOptions)options
|
||||
{
|
||||
/**
|
||||
* TODO: Proper move implementation.
|
||||
*/
|
||||
[self deleteItems:@[ indexPath ] animationOptions:options];
|
||||
[self insertItems:@[ newIndexPath ] animationOptions:options];
|
||||
}
|
||||
|
||||
- (void)moveSection:(NSInteger)section toSection:(NSInteger)newSection animationOptions:(ASDataControllerAnimationOptions)options
|
||||
{
|
||||
/**
|
||||
* TODO: Proper move implementation.
|
||||
*/
|
||||
[self deleteSections:[NSIndexSet indexSetWithIndex:section] animationOptions:options];
|
||||
[self insertSections:[NSIndexSet indexSetWithIndex:newSection] animationOptions:options];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (BOOL)_ensureNotCompleted
|
||||
|
||||
@@ -696,7 +696,7 @@
|
||||
XCTAssertTrue(toSection < originalSection);
|
||||
ASTestSectionContext *movedSectionContext = (ASTestSectionContext *)[cn contextForSection:toSection];
|
||||
XCTAssertNotNil(movedSectionContext);
|
||||
// ASCollectionView currently uses ASChangeSetDataController which splits a move operation to a pair of delete and insert ones.
|
||||
// ASCollectionView currently splits a move operation to a pair of delete and insert ones.
|
||||
// So this movedSectionContext is newly loaded and thus is second generation.
|
||||
XCTAssertEqual(movedSectionContext.sectionGeneration, 2);
|
||||
XCTAssertEqual(movedSectionContext.sectionIndex, toSection);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#import "ASTableView.h"
|
||||
#import "ASTableViewInternal.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASCellNode.h"
|
||||
#import "ASTableNode.h"
|
||||
#import "ASTableView+Undeprecated.h"
|
||||
@@ -24,7 +23,7 @@
|
||||
#define NumberOfSections 10
|
||||
#define NumberOfReloadIterations 50
|
||||
|
||||
@interface ASTestDataController : ASChangeSetDataController
|
||||
@interface ASTestDataController : ASDataController
|
||||
@property (nonatomic) int numberOfAllNodesRelayouts;
|
||||
@end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user