mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Implement ASChangeSetDataController.
This commit is contained in:
@@ -237,6 +237,14 @@
|
||||
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, ); }; };
|
||||
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 */; };
|
||||
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, ); }; };
|
||||
@@ -597,6 +605,10 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -890,6 +902,8 @@
|
||||
205F0E1C1B373A2C007741D0 /* ASCollectionViewLayoutController.mm */,
|
||||
464052191A3F83C40061C0BA /* ASDataController.h */,
|
||||
4640521A1A3F83C40061C0BA /* ASDataController.mm */,
|
||||
AC026B671BD57D6F00BBC17E /* ASChangeSetDataController.h */,
|
||||
AC026B681BD57D6F00BBC17E /* ASChangeSetDataController.m */,
|
||||
05A6D05819D0EB64002DD95E /* ASDealloc2MainObject.h */,
|
||||
05A6D05919D0EB64002DD95E /* ASDealloc2MainObject.m */,
|
||||
4640521B1A3F83C40061C0BA /* ASFlowLayoutController.h */,
|
||||
@@ -954,6 +968,8 @@
|
||||
058D0A01195D050800B7D73C /* Private */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AC026B6D1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h */,
|
||||
AC026B6E1BD57DBF00BBC17E /* _ASHierarchyChangeSet.m */,
|
||||
9C65A7291BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h */,
|
||||
9C8221931BA237B80037F19A /* ASStackBaselinePositionedLayout.h */,
|
||||
9C8221941BA237B80037F19A /* ASStackBaselinePositionedLayout.mm */,
|
||||
@@ -1075,6 +1091,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AC026B691BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
|
||||
058D0A71195D05F800B7D73C /* _AS-objc-internal.h in Headers */,
|
||||
058D0A68195D05EC00B7D73C /* _ASAsyncTransaction.h in Headers */,
|
||||
058D0A6A195D05EC00B7D73C /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
@@ -1133,6 +1150,7 @@
|
||||
292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */,
|
||||
ACF6ED261B17843500DA7C62 /* ASLayoutSpec.h in Headers */,
|
||||
ACF6ED4D1B17847A00DA7C62 /* ASLayoutSpecUtilities.h in Headers */,
|
||||
AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
|
||||
0516FA3D1A15563400B4EBED /* ASLog.h in Headers */,
|
||||
0442850D1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
||||
0516FA401A1563D200B4EBED /* ASMultiplexImageNode.h in Headers */,
|
||||
@@ -1182,6 +1200,7 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
AC026B6A1BD57D6F00BBC17E /* ASChangeSetDataController.h in Headers */,
|
||||
B35062481B010EFD0018CF92 /* _AS-objc-internal.h in Headers */,
|
||||
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
|
||||
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
|
||||
@@ -1202,6 +1221,7 @@
|
||||
B35062461B010EFD0018CF92 /* ASBasicImageDownloaderInternal.h in Headers */,
|
||||
B35062151B010EFD0018CF92 /* ASBatchContext.h in Headers */,
|
||||
044285081BAA63FE00D16268 /* ASBatchFetching.h in Headers */,
|
||||
AC026B701BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
|
||||
B35061F31B010EFD0018CF92 /* ASCellNode.h in Headers */,
|
||||
34EFC7631B701CBF00AD841F /* ASCenterLayoutSpec.h in Headers */,
|
||||
18C2ED7F1B9B7DE800F627B3 /* ASCollectionNode.h in Headers */,
|
||||
@@ -1478,6 +1498,7 @@
|
||||
058D0A23195D050800B7D73C /* _ASAsyncTransactionContainer.m in Sources */,
|
||||
058D0A24195D050800B7D73C /* _ASAsyncTransactionGroup.m in Sources */,
|
||||
058D0A26195D050800B7D73C /* _ASCoreAnimationExtras.mm in Sources */,
|
||||
AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */,
|
||||
058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */,
|
||||
058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */,
|
||||
9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */,
|
||||
@@ -1533,6 +1554,7 @@
|
||||
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */,
|
||||
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
|
||||
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
|
||||
AC026B6B1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
|
||||
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
|
||||
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
|
||||
058D0A1C195D050800B7D73C /* ASTextNodeCoreTextAdditions.m in Sources */,
|
||||
@@ -1593,6 +1615,7 @@
|
||||
9B92C8851BC2EB6E00EE46B2 /* ASCollectionDataController.mm in Sources */,
|
||||
B350623D1B010EFD0018CF92 /* _ASAsyncTransaction.m in Sources */,
|
||||
B35062401B010EFD0018CF92 /* _ASAsyncTransactionContainer.m in Sources */,
|
||||
AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */,
|
||||
B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */,
|
||||
B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */,
|
||||
2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */,
|
||||
@@ -1648,6 +1671,7 @@
|
||||
34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */,
|
||||
34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */,
|
||||
34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */,
|
||||
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
|
||||
34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */,
|
||||
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,
|
||||
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */,
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#import "ASTableView.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASDataController.h"
|
||||
#import "ASChangeSetDataController.h"
|
||||
#import "ASCollectionViewLayoutController.h"
|
||||
#import "ASLayoutController.h"
|
||||
#import "ASRangeController.h"
|
||||
@@ -210,7 +210,7 @@ void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)()) {
|
||||
_rangeController.layoutController = _layoutController;
|
||||
_rangeController.delegate = self;
|
||||
|
||||
_dataController = [[ASDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled];
|
||||
_dataController = [[ASChangeSetDataController alloc] initWithAsyncDataFetching:asyncDataFetchingEnabled];
|
||||
_dataController.dataSource = self;
|
||||
_dataController.delegate = _rangeController;
|
||||
|
||||
|
||||
19
AsyncDisplayKit/Details/ASChangeSetDataController.h
Normal file
19
AsyncDisplayKit/Details/ASChangeSetDataController.h
Normal 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
|
||||
179
AsyncDisplayKit/Details/ASChangeSetDataController.m
Normal file
179
AsyncDisplayKit/Details/ASChangeSetDataController.m
Normal 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
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASDataController.h>
|
||||
#import <AsyncDisplayKit/ASChangeSetDataController.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
|
||||
@class ASDisplayNode;
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
@end
|
||||
|
||||
@interface ASCollectionDataController : ASDataController
|
||||
@interface ASCollectionDataController : ASChangeSetDataController
|
||||
|
||||
- (ASCellNode *)supplementaryNodeOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath;
|
||||
|
||||
|
||||
@@ -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
|
||||
* 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;
|
||||
@interface ASDataController : ASDealloc2MainObject <ASFlowLayoutControllerDataSource>
|
||||
|
||||
@@ -26,3 +26,7 @@ CGFloat ASCeilPixelValue(CGFloat f);
|
||||
CGFloat ASRoundPixelValue(CGFloat f);
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
@interface NSIndexPath (ASInverseComparison)
|
||||
- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath;
|
||||
@end
|
||||
|
||||
@@ -70,3 +70,12 @@ CGFloat ASRoundPixelValue(CGFloat f)
|
||||
{
|
||||
return roundf(f * ASScreenScale()) / ASScreenScale();
|
||||
}
|
||||
|
||||
@implementation NSIndexPath (ASInverseComparison)
|
||||
|
||||
- (NSComparisonResult)as_inverseCompare:(NSIndexPath *)otherIndexPath
|
||||
{
|
||||
return [otherIndexPath compare:self];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
72
AsyncDisplayKit/Private/_ASHierarchyChangeSet.h
Normal file
72
AsyncDisplayKit/Private/_ASHierarchyChangeSet.h
Normal 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
|
||||
336
AsyncDisplayKit/Private/_ASHierarchyChangeSet.m
Normal file
336
AsyncDisplayKit/Private/_ASHierarchyChangeSet.m
Normal 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
|
||||
Reference in New Issue
Block a user