[ASRangeController] Initial implementation of functional-style, ASInterfaceState-based range controller.

This commit is contained in:
Scott Goodson
2016-01-01 00:48:44 -08:00
parent 7a9cd1f930
commit b273f358f5
26 changed files with 382 additions and 187 deletions

View File

@@ -459,6 +459,7 @@
D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; }; D785F6631A74327E00291744 /* ASScrollNode.m in Sources */ = {isa = PBXBuildFile; fileRef = D785F6611A74327E00291744 /* ASScrollNode.m */; };
DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; }; DB7121BCD50849C498C886FB /* libPods-AsyncDisplayKitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */; };
DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; }; DE040EF91C2B40AC004692FF /* ASCollectionViewFlowLayoutInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 251B8EF41BBB3D690087C538 /* ASCollectionViewFlowLayoutInspector.h */; settings = {ATTRIBUTES = (Public, ); }; };
DE0702FC1C3671E900D7DE62 /* libAsyncDisplayKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 058D09AC195D04C000B7D73C /* libAsyncDisplayKit.a */; };
DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; }; DE6EA3221C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; }; DE6EA3231C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */; };
DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; }; DE8BEAC11C2DF3FC00D57C12 /* ASDelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = DE8BEABF1C2DF3FC00D57C12 /* ASDelegateProxy.h */; };
@@ -469,6 +470,10 @@
DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; }; DECBD6E81BE56E1900CF4905 /* ASButtonNode.h in Headers */ = {isa = PBXBuildFile; fileRef = DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; }; DECBD6E91BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; }; DECBD6EA1BE56E1900CF4905 /* ASButtonNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */; };
DECC2ECD1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */ = {isa = PBXBuildFile; fileRef = DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */; };
DECC2ECE1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */ = {isa = PBXBuildFile; fileRef = DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */; };
DECC2ECF1C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */; };
DECC2ED01C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */ = {isa = PBXBuildFile; fileRef = DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -762,6 +767,8 @@
DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDelegateProxy.m; sourceTree = "<group>"; }; DE8BEAC01C2DF3FC00D57C12 /* ASDelegateProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDelegateProxy.m; sourceTree = "<group>"; };
DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; }; DECBD6E51BE56E1900CF4905 /* ASButtonNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASButtonNode.h; sourceTree = "<group>"; };
DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; }; DECBD6E61BE56E1900CF4905 /* ASButtonNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASButtonNode.mm; sourceTree = "<group>"; };
DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASRangeControllerBeta.h; sourceTree = "<group>"; };
DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASRangeControllerBeta.mm; sourceTree = "<group>"; };
EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; EFA731F0396842FF8AB635EE /* libPods-AsyncDisplayKitTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AsyncDisplayKitTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.debug.xcconfig"; sourceTree = "<group>"; }; FB07EABBCF28656C6297BC2D /* Pods-AsyncDisplayKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AsyncDisplayKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AsyncDisplayKitTests/Pods-AsyncDisplayKitTests.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -771,6 +778,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
DE0702FC1C3671E900D7DE62 /* libAsyncDisplayKit.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -1029,6 +1037,8 @@
058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */, 058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */,
055F1A3619ABD413004DAFF1 /* ASRangeController.h */, 055F1A3619ABD413004DAFF1 /* ASRangeController.h */,
055F1A3719ABD413004DAFF1 /* ASRangeController.mm */, 055F1A3719ABD413004DAFF1 /* ASRangeController.mm */,
DECC2ECB1C35C1C600388446 /* ASRangeControllerBeta.h */,
DECC2ECC1C35C1C600388446 /* ASRangeControllerBeta.mm */,
292C599C1A956527007E5DD6 /* ASRangeHandler.h */, 292C599C1A956527007E5DD6 /* ASRangeHandler.h */,
258FF4251C0D152600A83844 /* ASRangeHandlerVisible.h */, 258FF4251C0D152600A83844 /* ASRangeHandlerVisible.h */,
258FF4261C0D152600A83844 /* ASRangeHandlerVisible.mm */, 258FF4261C0D152600A83844 /* ASRangeHandlerVisible.mm */,
@@ -1315,6 +1325,7 @@
9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */, 9C65A72A1BA8EA4D0084DA91 /* ASLayoutOptionsPrivate.h in Headers */,
292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */, 292C599F1A956527007E5DD6 /* ASLayoutRangeType.h in Headers */,
257754B61BEE44CD00737CA5 /* ASEqualityHashHelpers.h in Headers */, 257754B61BEE44CD00737CA5 /* ASEqualityHashHelpers.h in Headers */,
DECC2ECD1C35C1C600388446 /* ASRangeControllerBeta.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 */, AC026B6F1BD57DBF00BBC17E /* _ASHierarchyChangeSet.h in Headers */,
@@ -1373,6 +1384,7 @@
B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */, B350623C1B010EFD0018CF92 /* _ASAsyncTransaction.h in Headers */,
B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */, B350623E1B010EFD0018CF92 /* _ASAsyncTransactionContainer+Private.h in Headers */,
B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */, B350623F1B010EFD0018CF92 /* _ASAsyncTransactionContainer.h in Headers */,
DECC2ECE1C35C1C600388446 /* ASRangeControllerBeta.h in Headers */,
254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */, 254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */,
B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */, B35062411B010EFD0018CF92 /* _ASAsyncTransactionGroup.h in Headers */,
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */, B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
@@ -1753,6 +1765,7 @@
257754BE1BEE458E00737CA5 /* ASTextKitHelpers.mm in Sources */, 257754BE1BEE458E00737CA5 /* ASTextKitHelpers.mm in Sources */,
257754A91BEE44CD00737CA5 /* ASTextKitContext.mm in Sources */, 257754A91BEE44CD00737CA5 /* ASTextKitContext.mm in Sources */,
ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */, ACF6ED501B17847A00DA7C62 /* ASStackPositionedLayout.mm in Sources */,
DECC2ECF1C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */,
ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */, ACF6ED521B17847A00DA7C62 /* ASStackUnpositionedLayout.mm in Sources */,
257754A61BEE44CD00737CA5 /* ASTextKitAttributes.mm in Sources */, 257754A61BEE44CD00737CA5 /* ASTextKitAttributes.mm in Sources */,
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */, ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
@@ -1883,6 +1896,7 @@
9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */, 9C8221981BA237B80037F19A /* ASStackBaselinePositionedLayout.mm in Sources */,
34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */, 34EFC7721B701D0300AD841F /* ASStackLayoutSpec.mm in Sources */,
34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */, 34EFC7761B701D2A00AD841F /* ASStackPositionedLayout.mm in Sources */,
DECC2ED01C35C1C600388446 /* ASRangeControllerBeta.mm in Sources */,
34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */, 34EFC7781B701D3100AD841F /* ASStackUnpositionedLayout.mm in Sources */,
AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */, AC026B6C1BD57D6F00BBC17E /* ASChangeSetDataController.m in Sources */,
34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */, 34EFC7741B701D0A00AD841F /* ASStaticLayoutSpec.mm in Sources */,

View File

@@ -14,6 +14,7 @@
#import "ASCollectionViewLayoutController.h" #import "ASCollectionViewLayoutController.h"
#import "ASCollectionViewFlowLayoutInspector.h" #import "ASCollectionViewFlowLayoutInspector.h"
#import "ASDisplayNode+FrameworkPrivate.h" #import "ASDisplayNode+FrameworkPrivate.h"
#import "ASDisplayNode+Beta.h"
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASRangeController.h" #import "ASRangeController.h"
#import "UICollectionViewLayout+ASConvenience.h" #import "UICollectionViewLayout+ASConvenience.h"
@@ -150,9 +151,12 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
self.strongCollectionNode = collectionNode; self.strongCollectionNode = collectionNode;
} }
_layoutController = [[ASCollectionViewLayoutController alloc] initWithCollectionView:self]; _layoutController = [ASDisplayNode shouldUseNewRenderingRange] ?
[[ASCollectionViewLayoutControllerBeta alloc] initWithCollectionView:self] :
[[ASCollectionViewLayoutControllerStable alloc] initWithCollectionView:self];
_rangeController = [[ASRangeController alloc] init]; _rangeController = [ASDisplayNode shouldUseNewRenderingRange] ? [[ASRangeControllerBeta alloc] init]
: [[ASRangeControllerStable alloc] init];
_rangeController.dataSource = self; _rangeController.dataSource = self;
_rangeController.delegate = self; _rangeController.delegate = self;
_rangeController.layoutController = _layoutController; _rangeController.layoutController = _layoutController;
@@ -319,22 +323,22 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{ {
[_layoutController setTuningParameters:tuningParameters forRangeType:rangeType]; [_rangeController setTuningParameters:tuningParameters forRangeType:rangeType];
} }
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
{ {
return [_layoutController tuningParametersForRangeType:rangeType]; return [_rangeController tuningParametersForRangeType:rangeType];
} }
- (ASRangeTuningParameters)rangeTuningParameters - (ASRangeTuningParameters)rangeTuningParameters
{ {
return [self tuningParametersForRangeType:ASLayoutRangeTypeRender]; return [self tuningParametersForRangeType:ASLayoutRangeTypeDisplay];
} }
- (void)setRangeTuningParameters:(ASRangeTuningParameters)tuningParameters - (void)setRangeTuningParameters:(ASRangeTuningParameters)tuningParameters
{ {
[self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; [self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeDisplay];
} }
- (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath - (CGSize)calculatedSizeForNodeAtIndexPath:(NSIndexPath *)indexPath
@@ -775,6 +779,11 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
return [_dataController nodesAtIndexPaths:indexPaths]; return [_dataController nodesAtIndexPaths:indexPaths];
} }
- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath
{
return [_dataController nodeAtIndexPath:indexPath];
}
#pragma mark - ASRangeControllerDelegate #pragma mark - ASRangeControllerDelegate
- (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController - (void)didBeginUpdatesInRangeController:(ASRangeController *)rangeController

View File

@@ -203,20 +203,26 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
static NSMutableSet *nodesToDisplay = nil; static NSMutableSet *nodesToDisplay = nil;
static BOOL displayScheduled = NO; static BOOL displayScheduled = NO;
static ASDN::RecursiveMutex displaySchedulerLock;
{
ASDN::MutexLocker l(displaySchedulerLock);
if (!nodesToDisplay) { if (!nodesToDisplay) {
nodesToDisplay = [[NSMutableSet alloc] init]; nodesToDisplay = [[NSMutableSet alloc] init];
} }
[nodesToDisplay addObject:node]; [nodesToDisplay addObject:node];
}
if (!displayScheduled) { if (!displayScheduled) {
displayScheduled = YES; displayScheduled = YES;
// It's essenital that any layout pass that is scheduled during the current // It's essenital that any layout pass that is scheduled during the current
// runloop has a chance to be applied / scheduled, so always perform this after the current runloop. // runloop has a chance to be applied / scheduled, so always perform this after the current runloop.
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
ASDN::MutexLocker l(displaySchedulerLock);
displayScheduled = NO; displayScheduled = NO;
for (ASDisplayNode *node in nodesToDisplay) { NSSet *displayingNodes = [nodesToDisplay copy];
nodesToDisplay = nil;
for (ASDisplayNode *node in displayingNodes) {
[node __recursivelyTriggerDisplayAndBlock:NO]; [node __recursivelyTriggerDisplayAndBlock:NO];
} }
nodesToDisplay = nil;
}); });
} }
} }
@@ -1835,6 +1841,15 @@ static BOOL ShouldUseNewRenderingRange = NO;
}); });
} }
- (void)recursivelySetInterfaceState:(ASInterfaceState)interfaceState
{
ASDisplayNodePerformBlockOnEveryNode(nil, self, ^(ASDisplayNode *node) {
node.interfaceState = interfaceState;
});
// FIXME: This should also be called in setInterfaceState: if it isn't being applied recursively.
[ASDisplayNode scheduleNodeForDisplay:self];
}
- (ASHierarchyState)hierarchyState - (ASHierarchyState)hierarchyState
{ {
ASDN::MutexLocker l(_propertyLock); ASDN::MutexLocker l(_propertyLock);

View File

@@ -9,6 +9,7 @@
#import "ASPagerNode.h" #import "ASPagerNode.h"
#import "ASDelegateProxy.h" #import "ASDelegateProxy.h"
#import "ASDisplayNode+Subclasses.h" #import "ASDisplayNode+Subclasses.h"
#import "UICollectionViewLayout+ASConvenience.h"
@interface ASPagerNode () <ASCollectionDataSource, ASCollectionViewDelegateFlowLayout, ASDelegateProxyInterceptor> @interface ASPagerNode () <ASCollectionDataSource, ASCollectionViewDelegateFlowLayout, ASDelegateProxyInterceptor>
{ {
@@ -34,7 +35,7 @@
- (instancetype)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)flowLayout; - (instancetype)initWithCollectionViewLayout:(UICollectionViewFlowLayout *)flowLayout;
{ {
ASDisplayNodeAssert([flowLayout isKindOfClass:[UICollectionViewFlowLayout class]], @"ASPagerNode requires a flow layout."); ASDisplayNodeAssert([flowLayout asdk_isFlowLayout], @"ASPagerNode requires a flow layout.");
self = [super initWithCollectionViewLayout:flowLayout]; self = [super initWithCollectionViewLayout:flowLayout];
if (self != nil) { if (self != nil) {
_flowLayout = flowLayout; _flowLayout = flowLayout;
@@ -61,8 +62,8 @@
ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 }; ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 };
ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 }; ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
[self setTuningParameters:preloadParams forRangeType:ASLayoutRangeTypePreload]; [self setTuningParameters:preloadParams forRangeType:ASLayoutRangeTypeFetchData];
[self setTuningParameters:renderParams forRangeType:ASLayoutRangeTypeRender]; [self setTuningParameters:renderParams forRangeType:ASLayoutRangeTypeDisplay];
} }
#pragma mark - Helpers #pragma mark - Helpers

View File

@@ -13,6 +13,7 @@
#import "ASChangeSetDataController.h" #import "ASChangeSetDataController.h"
#import "ASCollectionViewLayoutController.h" #import "ASCollectionViewLayoutController.h"
#import "ASDelegateProxy.h" #import "ASDelegateProxy.h"
#import "ASDisplayNode+Beta.h"
#import "ASDisplayNode+FrameworkPrivate.h" #import "ASDisplayNode+FrameworkPrivate.h"
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASLayout.h" #import "ASLayout.h"
@@ -85,7 +86,10 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (instancetype)_initWithTableView:(ASTableView *)tableView; - (instancetype)_initWithTableView:(ASTableView *)tableView;
@end @end
@interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate, ASDataControllerSource, _ASTableViewCellDelegate, ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor> { @interface ASTableView () <ASRangeControllerDataSource, ASRangeControllerDelegate,
ASDataControllerSource, _ASTableViewCellDelegate,
ASCellNodeLayoutDelegate, ASDelegateProxyInterceptor>
{
ASTableViewProxy *_proxyDataSource; ASTableViewProxy *_proxyDataSource;
ASTableViewProxy *_proxyDelegate; ASTableViewProxy *_proxyDelegate;
@@ -139,7 +143,8 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
{ {
_layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical]; _layoutController = [[ASFlowLayoutController alloc] initWithScrollOption:ASFlowLayoutDirectionVertical];
_rangeController = [[ASRangeController alloc] init]; _rangeController = [ASDisplayNode shouldUseNewRenderingRange] ? [[ASRangeControllerBeta alloc] init]
: [[ASRangeControllerStable alloc] init];
_rangeController.layoutController = _layoutController; _rangeController.layoutController = _layoutController;
_rangeController.dataSource = self; _rangeController.dataSource = self;
_rangeController.delegate = self; _rangeController.delegate = self;
@@ -317,12 +322,12 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (ASRangeTuningParameters)rangeTuningParameters - (ASRangeTuningParameters)rangeTuningParameters
{ {
return [self tuningParametersForRangeType:ASLayoutRangeTypeRender]; return [self tuningParametersForRangeType:ASLayoutRangeTypeDisplay];
} }
- (void)setRangeTuningParameters:(ASRangeTuningParameters)tuningParameters - (void)setRangeTuningParameters:(ASRangeTuningParameters)tuningParameters
{ {
[self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; [self setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeDisplay];
} }
- (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)nodeForRowAtIndexPath:(NSIndexPath *)indexPath
@@ -684,6 +689,11 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
return [_dataController nodesAtIndexPaths:indexPaths]; return [_dataController nodesAtIndexPaths:indexPaths];
} }
- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath
{
return [_dataController nodeAtIndexPath:indexPath];
}
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController - (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();

View File

@@ -13,10 +13,6 @@ NS_ASSUME_NONNULL_BEGIN
@interface ASAbstractLayoutController : NSObject <ASLayoutController> @interface ASAbstractLayoutController : NSObject <ASLayoutController>
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -14,6 +14,7 @@
@interface ASAbstractLayoutController () { @interface ASAbstractLayoutController () {
std::vector<ASRangeTuningParameters> _tuningParameters; std::vector<ASRangeTuningParameters> _tuningParameters;
CGSize _viewportSize;
} }
@end @end
@@ -30,11 +31,11 @@
.leadingBufferScreenfuls = 0, .leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0 .trailingBufferScreenfuls = 0
}; };
_tuningParameters[ASLayoutRangeTypeRender] = { _tuningParameters[ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.5, .leadingBufferScreenfuls = 1.5,
.trailingBufferScreenfuls = 0.75 .trailingBufferScreenfuls = 0.75
}; };
_tuningParameters[ASLayoutRangeTypePreload] = { _tuningParameters[ASLayoutRangeTypeFetchData] = {
.leadingBufferScreenfuls = 3, .leadingBufferScreenfuls = 3,
.trailingBufferScreenfuls = 2 .trailingBufferScreenfuls = 2
}; };
@@ -60,16 +61,27 @@
#pragma mark - Abstract Index Path Range Support #pragma mark - Abstract Index Path Range Support
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType // FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths rangeType:(ASLayoutRangeType)rangeType
{ {
ASDisplayNodeAssertNotSupported(); ASDisplayNodeAssertNotSupported();
return NO; return NO;
} }
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType - (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType
{ {
ASDisplayNodeAssertNotSupported(); ASDisplayNodeAssertNotSupported();
return nil; return nil;
} }
- (void)setViewportSize:(CGSize)viewportSize
{
_viewportSize = viewportSize;
}
- (CGSize)viewportSize
{
return _viewportSize;
}
@end @end

View File

@@ -19,4 +19,10 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@interface ASCollectionViewLayoutControllerStable : ASCollectionViewLayoutController
@end
@interface ASCollectionViewLayoutControllerBeta : ASCollectionViewLayoutController
@end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -15,36 +15,6 @@
#import "CGRect+ASConvenience.h" #import "CGRect+ASConvenience.h"
#import "UICollectionViewLayout+ASConvenience.h" #import "UICollectionViewLayout+ASConvenience.h"
struct ASDirectionalScreenfulBuffer {
CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space.
CGFloat negativeDirection;
};
typedef struct ASDirectionalScreenfulBuffer ASDirectionalScreenfulBuffer;
ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection,
ASRangeTuningParameters rangeTuningParameters)
{
ASDirectionalScreenfulBuffer horizontalBuffer = {0, 0};
BOOL movingRight = ASScrollDirectionContainsRight(scrollDirection);
horizontalBuffer.positiveDirection = movingRight ? rangeTuningParameters.leadingBufferScreenfuls :
rangeTuningParameters.trailingBufferScreenfuls;
horizontalBuffer.negativeDirection = movingRight ? rangeTuningParameters.trailingBufferScreenfuls :
rangeTuningParameters.leadingBufferScreenfuls;
return horizontalBuffer;
}
ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection,
ASRangeTuningParameters rangeTuningParameters)
{
ASDirectionalScreenfulBuffer verticalBuffer = {0, 0};
BOOL movingDown = ASScrollDirectionContainsDown(scrollDirection);
verticalBuffer.positiveDirection = movingDown ? rangeTuningParameters.leadingBufferScreenfuls :
rangeTuningParameters.trailingBufferScreenfuls;
verticalBuffer.negativeDirection = movingDown ? rangeTuningParameters.trailingBufferScreenfuls :
rangeTuningParameters.leadingBufferScreenfuls;
return verticalBuffer;
}
struct ASRangeGeometry { struct ASRangeGeometry {
CGRect rangeBounds; CGRect rangeBounds;
CGRect updateBounds; CGRect updateBounds;
@@ -57,9 +27,9 @@ typedef struct ASRangeGeometry ASRangeGeometry;
@interface ASCollectionViewLayoutController () @interface ASCollectionViewLayoutController ()
{ {
@package
ASCollectionView * __weak _collectionView; ASCollectionView * __weak _collectionView;
UICollectionViewLayout * __strong _collectionViewLayout; UICollectionViewLayout * __strong _collectionViewLayout;
std::vector<CGRect> _updateRangeBoundsIndexedByRangeType;
ASScrollDirection _scrollableDirections; ASScrollDirection _scrollableDirections;
} }
@end @end
@@ -75,63 +45,34 @@ typedef struct ASRangeGeometry ASRangeGeometry;
_scrollableDirections = [collectionView scrollableDirections]; _scrollableDirections = [collectionView scrollableDirections];
_collectionView = collectionView; _collectionView = collectionView;
_collectionViewLayout = [collectionView collectionViewLayout]; _collectionViewLayout = [collectionView collectionViewLayout];
return self;
}
@end
@implementation ASCollectionViewLayoutControllerStable
{
std::vector<CGRect> _updateRangeBoundsIndexedByRangeType;
}
- (instancetype)initWithCollectionView:(ASCollectionView *)collectionView
{
if (!(self = [super initWithCollectionView:collectionView])) {
return nil;
}
_updateRangeBoundsIndexedByRangeType = std::vector<CGRect>(ASLayoutRangeTypeCount); _updateRangeBoundsIndexedByRangeType = std::vector<CGRect>(ASLayoutRangeTypeCount);
return self; return self;
} }
#pragma mark - - (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType
#pragma mark Index Paths in Range
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection
viewportSize:(CGSize)viewportSize
rangeType:(ASLayoutRangeType)rangeType
{ {
ASRangeGeometry rangeGeometry = [self rangeGeometryWithScrollDirection:scrollDirection ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType];
rangeTuningParameters:[self tuningParametersForRangeType:rangeType]]; ASRangeGeometry rangeGeometry = [self rangeGeometryWithScrollDirection:scrollDirection tuningParameters:tuningParameters];
_updateRangeBoundsIndexedByRangeType[rangeType] = rangeGeometry.updateBounds; _updateRangeBoundsIndexedByRangeType[rangeType] = rangeGeometry.updateBounds;
return [self indexPathsForItemsWithinRangeBounds:rangeGeometry.rangeBounds]; return [self indexPathsForItemsWithinRangeBounds:rangeGeometry.rangeBounds];
} }
- (ASRangeGeometry)rangeGeometryWithScrollDirection:(ASScrollDirection)scrollDirection
rangeTuningParameters:(ASRangeTuningParameters)rangeTuningParameters
{
CGRect rangeBounds = _collectionView.bounds;
CGRect updateBounds = _collectionView.bounds;
//scrollable directions can change for non-flow layouts
if ([_collectionViewLayout asdk_isFlowLayout] == NO) {
_scrollableDirections = [_collectionView scrollableDirections];
}
BOOL canScrollHorizontally = ASScrollDirectionContainsHorizontalDirection(_scrollableDirections);
if (canScrollHorizontally) {
ASDirectionalScreenfulBuffer horizontalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection,
rangeTuningParameters);
rangeBounds = asdk_CGRectExpandHorizontally(rangeBounds,
horizontalBuffer.negativeDirection,
horizontalBuffer.positiveDirection);
// Update bounds is at most 95% of the next/previous screenful and at least half of tuning parameter value.
updateBounds = asdk_CGRectExpandHorizontally(updateBounds,
MIN(horizontalBuffer.negativeDirection * 0.5, 0.95),
MIN(horizontalBuffer.positiveDirection * 0.5, 0.95));
}
BOOL canScrollVertically = ASScrollDirectionContainsVerticalDirection(_scrollableDirections);
if (canScrollVertically) {
ASDirectionalScreenfulBuffer verticalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection,
rangeTuningParameters);
rangeBounds = asdk_CGRectExpandVertically(rangeBounds,
verticalBuffer.negativeDirection,
verticalBuffer.positiveDirection);
// Update bounds is at most 95% of the next/previous screenful and at least half of tuning parameter value.
updateBounds = asdk_CGRectExpandVertically(updateBounds,
MIN(verticalBuffer.negativeDirection * 0.5, 0.95),
MIN(verticalBuffer.positiveDirection * 0.5, 0.95));
}
return {rangeBounds, updateBounds};
}
- (NSSet *)indexPathsForItemsWithinRangeBounds:(CGRect)rangeBounds - (NSSet *)indexPathsForItemsWithinRangeBounds:(CGRect)rangeBounds
{ {
NSMutableSet *indexPathSet = [[NSMutableSet alloc] init]; NSMutableSet *indexPathSet = [[NSMutableSet alloc] init];
@@ -144,13 +85,31 @@ typedef struct ASRangeGeometry ASRangeGeometry;
return indexPathSet; return indexPathSet;
} }
#pragma mark - - (ASRangeGeometry)rangeGeometryWithScrollDirection:(ASScrollDirection)scrollDirection
#pragma mark Should Update Range tuningParameters:(ASRangeTuningParameters)tuningParameters
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths
viewportSize:(CGSize)viewportSize
rangeType:(ASLayoutRangeType)rangeType
{ {
CGRect rangeBounds = _collectionView.bounds;
CGRect updateBounds = _collectionView.bounds;
// Scrollable directions can change for non-flow layouts
if ([_collectionViewLayout asdk_isFlowLayout] == NO) {
_scrollableDirections = [_collectionView scrollableDirections];
}
rangeBounds = CGRectExpandToRangeWithScrollableDirections(rangeBounds, tuningParameters, _scrollableDirections, scrollDirection);
ASRangeTuningParameters updateTuningParameters = tuningParameters;
updateTuningParameters.leadingBufferScreenfuls = MIN(updateTuningParameters.leadingBufferScreenfuls * 0.5, 0.95);
updateTuningParameters.trailingBufferScreenfuls = MIN(updateTuningParameters.trailingBufferScreenfuls * 0.5, 0.95);
updateBounds = CGRectExpandToRangeWithScrollableDirections(updateBounds, updateTuningParameters, _scrollableDirections, scrollDirection);
return {rangeBounds, updateBounds};
}
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths rangeType:(ASLayoutRangeType)rangeType
{
CGSize viewportSize = [self viewportSize];
CGRect updateRangeBounds = _updateRangeBoundsIndexedByRangeType[rangeType]; CGRect updateRangeBounds = _updateRangeBoundsIndexedByRangeType[rangeType];
if (CGRectIsEmpty(updateRangeBounds)) { if (CGRectIsEmpty(updateRangeBounds)) {
return YES; return YES;
@@ -169,3 +128,40 @@ typedef struct ASRangeGeometry ASRangeGeometry;
} }
@end @end
@implementation ASCollectionViewLayoutControllerBeta
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType
{
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType];
CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters];
return [self indexPathsForItemsWithinRangeBounds:rangeBounds];
}
- (NSSet *)indexPathsForItemsWithinRangeBounds:(CGRect)rangeBounds
{
NSArray *layoutAttributes = [_collectionViewLayout layoutAttributesForElementsInRect:rangeBounds];
NSMutableSet *indexPathSet = [NSMutableSet setWithCapacity:layoutAttributes.count];
for (UICollectionViewLayoutAttributes *la in layoutAttributes) {
//ASDisplayNodeAssert(![indexPathSet containsObject:la.indexPath], @"Shouldn't already contain indexPath");
ASDisplayNodeAssert(la.representedElementCategory != UICollectionElementCategoryDecorationView, @"UICollectionView decoration views are not supported by ASCollectionView");
[indexPathSet addObject:la.indexPath];
}
return indexPathSet;
}
- (CGRect)rangeBoundsWithScrollDirection:(ASScrollDirection)scrollDirection
rangeTuningParameters:(ASRangeTuningParameters)tuningParameters
{
CGRect rect = _collectionView.bounds;
// Scrollable directions can change for non-flow layouts
if ([_collectionViewLayout asdk_isFlowLayout] == NO) {
_scrollableDirections = [_collectionView scrollableDirections];
}
return CGRectExpandToRangeWithScrollableDirections(rect, tuningParameters, _scrollableDirections, scrollDirection);
}
@end

View File

@@ -39,7 +39,8 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
#pragma mark - Visible Indices #pragma mark - Visible Indices
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType // FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray *)indexPaths rangeType:(ASLayoutRangeType)rangeType
{ {
if (!indexPaths.count || rangeType >= _rangesByType.size()) { if (!indexPaths.count || rangeType >= _rangesByType.size()) {
return NO; return NO;
@@ -73,10 +74,11 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
* IndexPath array for the element in the working range. * IndexPath array for the element in the working range.
*/ */
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType - (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType
{ {
CGFloat viewportScreenMetric; CGFloat viewportScreenMetric;
ASScrollDirection leadingDirection; ASScrollDirection leadingDirection;
CGSize viewportSize = [self viewportSize];
if (_layoutDirection == ASFlowLayoutDirectionHorizontal) { if (_layoutDirection == ASFlowLayoutDirectionHorizontal) {
ASDisplayNodeAssert(scrollDirection == ASScrollDirectionNone || scrollDirection == ASScrollDirectionLeft || scrollDirection == ASScrollDirectionRight, @"Invalid scroll direction"); ASDisplayNodeAssert(scrollDirection == ASScrollDirectionNone || scrollDirection == ASScrollDirectionLeft || scrollDirection == ASScrollDirectionRight, @"Invalid scroll direction");

View File

@@ -28,11 +28,14 @@ typedef struct {
* *
* Defaults to a trailing buffer of one screenful and a leading buffer of two screenfuls. * Defaults to a trailing buffer of one screenful and a leading buffer of two screenfuls.
*/ */
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType; - (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray<NSIndexPath *> *)indexPaths viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType; // FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray<NSIndexPath *> *)indexPaths rangeType:(ASLayoutRangeType)rangeType;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection viewportSize:(CGSize)viewportSize rangeType:(ASLayoutRangeType)rangeType; - (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType;
@optional @optional
@@ -46,6 +49,9 @@ typedef struct {
- (void)setVisibleNodeIndexPaths:(NSArray<NSIndexPath *> *)indexPaths; - (void)setVisibleNodeIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)setViewportSize:(CGSize)viewportSize;
- (CGSize)viewportSize;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -10,7 +10,10 @@
typedef NS_ENUM(NSInteger, ASLayoutRangeType) { typedef NS_ENUM(NSInteger, ASLayoutRangeType) {
ASLayoutRangeTypeVisible = 0, ASLayoutRangeTypeVisible = 0,
ASLayoutRangeTypeRender, ASLayoutRangeTypeDisplay,
ASLayoutRangeTypePreload, ASLayoutRangeTypeFetchData,
ASLayoutRangeTypeCount ASLayoutRangeTypeCount
}; };
#define ASLayoutRangeTypeRender ASLayoutRangeTypeDisplay
#define ASLayoutRangeTypePreload ASLayoutRangeTypeFetchData

View File

@@ -26,6 +26,11 @@ NS_ASSUME_NONNULL_BEGIN
* This includes cancelling those asynchronous operations as cells fall outside of the working ranges. * This includes cancelling those asynchronous operations as cells fall outside of the working ranges.
*/ */
@interface ASRangeController : ASDealloc2MainObject <ASDataControllerDelegate> @interface ASRangeController : ASDealloc2MainObject <ASDataControllerDelegate>
{
id<ASLayoutController> _layoutController;
__weak id<ASRangeControllerDataSource> _dataSource;
__weak id<ASRangeControllerDelegate> _delegate;
}
/** /**
* Notify the range controller that the visible range has been updated. * Notify the range controller that the visible range has been updated.
@@ -46,6 +51,9 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node; - (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node;
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
/** /**
* An object that describes the layout behavior of the ranged component (table view, collection view, etc.) * An object that describes the layout behavior of the ranged component (table view, collection view, etc.)
* *
@@ -66,6 +74,12 @@ NS_ASSUME_NONNULL_BEGIN
@end @end
@interface ASRangeControllerStable : ASRangeController
@end
@interface ASRangeControllerBeta : ASRangeController
@end
/** /**
* Data source for ASRangeController. * Data source for ASRangeController.
* *
@@ -88,15 +102,10 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController; - (CGSize)viewportSizeForRangeController:(ASRangeController *)rangeController;
/**
* Fetch nodes at specific index paths.
*
* @param rangeController Sender.
*
* @param indexPaths Index paths.
*/
- (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths; - (NSArray *)rangeController:(ASRangeController *)rangeController nodesAtIndexPaths:(NSArray *)indexPaths;
- (ASDisplayNode *)rangeController:(ASRangeController *)rangeController nodeAtIndexPath:(NSIndexPath *)indexPath;
@end @end
/** /**

View File

@@ -15,8 +15,33 @@
#import "ASRangeHandlerRender.h" #import "ASRangeHandlerRender.h"
#import "ASRangeHandlerPreload.h" #import "ASRangeHandlerPreload.h"
#import "ASInternalHelpers.h" #import "ASInternalHelpers.h"
#import "ASLayoutController.h"
#import "ASLayoutRangeType.h"
@interface ASRangeController () { @implementation ASRangeController
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
{
}
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node
{
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{
[_layoutController setTuningParameters:tuningParameters forRangeType:rangeType];
}
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
{
return [_layoutController tuningParametersForRangeType:rangeType];
}
@end
@interface ASRangeControllerStable ()
{
BOOL _rangeIsValid; BOOL _rangeIsValid;
// keys should be ASLayoutRangeTypes and values NSSets containing NSIndexPaths // keys should be ASLayoutRangeTypes and values NSSets containing NSIndexPaths
@@ -29,19 +54,21 @@
@end @end
@implementation ASRangeController @implementation ASRangeControllerStable
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
- (instancetype)init {
self = [super init];
if (self != nil) {
_rangeIsValid = YES; _rangeIsValid = YES;
_rangeTypeIndexPaths = [NSMutableDictionary dictionary]; _rangeTypeIndexPaths = [NSMutableDictionary dictionary];
_rangeTypeHandlers = @{ _rangeTypeHandlers = @{
@(ASLayoutRangeTypeVisible) : [[ASRangeHandlerVisible alloc] init], @(ASLayoutRangeTypeVisible) : [[ASRangeHandlerVisible alloc] init],
@(ASLayoutRangeTypeRender): [[ASRangeHandlerRender alloc] init], @(ASLayoutRangeTypeDisplay) : [[ASRangeHandlerRender alloc] init],
@(ASLayoutRangeTypePreload): [[ASRangeHandlerPreload alloc] init], @(ASLayoutRangeTypeFetchData): [[ASRangeHandlerPreload alloc] init],
}; };
}
return self; return self;
} }
@@ -111,6 +138,7 @@
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths]; NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
CGSize viewportSize = [_dataSource viewportSizeForRangeController:self]; CGSize viewportSize = [_dataSource viewportSizeForRangeController:self];
[_layoutController setViewportSize:viewportSize];
// the layout controller needs to know what the current visible indices are to calculate range offsets // the layout controller needs to know what the current visible indices are to calculate range offsets
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) { if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
@@ -124,10 +152,8 @@
// this delegate decide what happens when a node is added or removed from a range // this delegate decide what happens when a node is added or removed from a range
id<ASRangeHandler> rangeHandler = _rangeTypeHandlers[rangeKey]; id<ASRangeHandler> rangeHandler = _rangeTypeHandlers[rangeKey];
if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths viewportSize:viewportSize rangeType:rangeType]) { if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths rangeType:rangeType]) {
NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:rangeType];
viewportSize:viewportSize
rangeType:rangeType];
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths // Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
// This value may be nil for the first call of this method. // This value may be nil for the first call of this method.
@@ -172,7 +198,7 @@
- (BOOL)shouldSkipVisibleNodesForRangeType:(ASLayoutRangeType)rangeType - (BOOL)shouldSkipVisibleNodesForRangeType:(ASLayoutRangeType)rangeType
{ {
return rangeType == ASLayoutRangeTypeRender; return rangeType == ASLayoutRangeTypeDisplay;
} }
#pragma mark - ASDataControllerDelegete #pragma mark - ASDataControllerDelegete

View File

@@ -72,25 +72,18 @@
} }
CGSize viewportSize = [_dataSource viewportSizeForRangeController:self]; CGSize viewportSize = [_dataSource viewportSizeForRangeController:self];
[_layoutController setViewportSize:viewportSize];
// the layout controller needs to know what the current visible indices are to calculate range offsets // the layout controller needs to know what the current visible indices are to calculate range offsets
if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) { if ([_layoutController respondsToSelector:@selector(setVisibleNodeIndexPaths:)]) {
[_layoutController setVisibleNodeIndexPaths:visibleNodePaths]; [_layoutController setVisibleNodeIndexPaths:visibleNodePaths];
} }
NSSet *fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection NSSet *fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeFetchData];
viewportSize:viewportSize NSSet *displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeDisplay];
rangeType:ASLayoutRangeTypeFetchData]; NSSet *visibleIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeVisible];
NSSet *displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection //NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
viewportSize:viewportSize
rangeType:ASLayoutRangeTypeDisplay];
NSSet *visibleIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
viewportSize:viewportSize
rangeType:ASLayoutRangeTypeVisible];
NSSet *visibleNodePathsSet = [NSSet setWithArray:visibleNodePaths];
//NSLog(@"visible sets are equal: %d", [visibleIndexPaths isEqualToSet:visibleNodePathsSet]); //NSLog(@"visible sets are equal: %d", [visibleIndexPaths isEqualToSet:visibleNodePathsSet]);
// Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint. // Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.

View File

@@ -14,13 +14,13 @@
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType - (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
{ {
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypePreload, @"Preload delegate should not handle other ranges"); ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeFetchData, @"Preload delegate should not handle other ranges");
[node enterInterfaceState:ASInterfaceStateFetchData]; [node enterInterfaceState:ASInterfaceStateFetchData];
} }
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType - (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
{ {
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypePreload, @"Preload delegate should not handle other ranges"); ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeFetchData, @"Preload delegate should not handle other ranges");
[node exitInterfaceState:ASInterfaceStateFetchData]; [node exitInterfaceState:ASInterfaceStateFetchData];
} }

View File

@@ -44,7 +44,7 @@
if (![ASDisplayNode shouldUseNewRenderingRange]) { if (![ASDisplayNode shouldUseNewRenderingRange]) {
for (CALayer *layer in [self.workingWindow.layer.sublayers copy]) { for (CALayer *layer in [self.workingWindow.layer.sublayers copy]) {
ASDisplayNode *node = layer.asyncdisplaykit_node; ASDisplayNode *node = layer.asyncdisplaykit_node;
[self node:node exitedRangeOfType:ASLayoutRangeTypeRender]; [self node:node exitedRangeOfType:ASLayoutRangeTypeDisplay];
} }
} }
} }
@@ -52,7 +52,7 @@
- (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType - (void)node:(ASDisplayNode *)node enteredRangeOfType:(ASLayoutRangeType)rangeType
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges"); ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeDisplay, @"Render delegate should not handle other ranges");
// If a node had previously been onscreen but now is only in the working range, // If a node had previously been onscreen but now is only in the working range,
// ensure its view is not orphaned in a UITableViewCell in the reuse pool. // ensure its view is not orphaned in a UITableViewCell in the reuse pool.
@@ -64,6 +64,7 @@
[node enterInterfaceState:ASInterfaceStateDisplay]; [node enterInterfaceState:ASInterfaceStateDisplay];
ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
if ([ASDisplayNode shouldUseNewRenderingRange]) { if ([ASDisplayNode shouldUseNewRenderingRange]) {
[node recursivelyEnsureDisplaySynchronously:NO]; [node recursivelyEnsureDisplaySynchronously:NO];
} else { } else {
@@ -79,7 +80,7 @@
- (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType - (void)node:(ASDisplayNode *)node exitedRangeOfType:(ASLayoutRangeType)rangeType
{ {
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeRender, @"Render delegate should not handle other ranges"); ASDisplayNodeAssert(rangeType == ASLayoutRangeTypeDisplay, @"Render delegate should not handle other ranges");
// This code is tricky. There are several possible states a node can be in when it reaches this point. // This code is tricky. There are several possible states a node can be in when it reaches this point.
// 1. Layer-backed vs view-backed nodes. AS of this writing, only ASCellNodes arrive here, which are always view-backed — // 1. Layer-backed vs view-backed nodes. AS of this writing, only ASCellNodes arrive here, which are always view-backed —
@@ -101,6 +102,8 @@
// The node calls clearCurrentContents and suspends display // The node calls clearCurrentContents and suspends display
[node exitInterfaceState:ASInterfaceStateDisplay]; [node exitInterfaceState:ASInterfaceStateDisplay];
ASDisplayNodeAssert(![ASDisplayNode shouldUseNewRenderingRange], @"It should no longer be possible to reach this point with the new display range enabled");
if ([ASDisplayNode shouldUseNewRenderingRange]) { if ([ASDisplayNode shouldUseNewRenderingRange]) {
if (![node isLayerBacked]) { if (![node isLayerBacked]) {
[node.view removeFromSuperview]; [node.view removeFromSuperview];

View File

@@ -10,13 +10,16 @@
#import <CoreGraphics/CoreGraphics.h> #import <CoreGraphics/CoreGraphics.h>
#import "ASBaseDefines.h" #import "ASBaseDefines.h"
#import "ASLayoutController.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN ASDISPLAYNODE_EXTERN_C_BEGIN
CGRect asdk_CGRectExpandHorizontally(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier); CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect,
CGRect asdk_CGRectExpandVertically(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier); ASRangeTuningParameters tuningParameters,
ASScrollDirection scrollableDirections,
ASScrollDirection scrollDirection);
ASDISPLAYNODE_EXTERN_C_END ASDISPLAYNODE_EXTERN_C_END

View File

@@ -7,25 +7,74 @@
*/ */
#import "CGRect+ASConvenience.h" #import "CGRect+ASConvenience.h"
#import "ASScrollDirection.h"
#import "ASLayoutController.h"
CGRect asdk_CGRectExpandHorizontally(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier) { struct ASDirectionalScreenfulBuffer {
CGFloat negativeDirectionWidth = negativeMultiplier * rect.size.width; CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space.
CGFloat positiveDirectionWidth = positiveMultiplier * rect.size.width; CGFloat negativeDirection;
CGFloat width = negativeDirectionWidth + rect.size.width + positiveDirectionWidth; };
CGFloat originX = rect.origin.x - negativeDirectionWidth; typedef struct ASDirectionalScreenfulBuffer ASDirectionalScreenfulBuffer;
return CGRectMake(originX,
rect.origin.y, ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection,
width, ASRangeTuningParameters rangeTuningParameters)
rect.size.height); {
ASDirectionalScreenfulBuffer horizontalBuffer = {0, 0};
BOOL movingRight = ASScrollDirectionContainsRight(scrollDirection);
horizontalBuffer.positiveDirection = movingRight ? rangeTuningParameters.leadingBufferScreenfuls
: rangeTuningParameters.trailingBufferScreenfuls;
horizontalBuffer.negativeDirection = movingRight ? rangeTuningParameters.trailingBufferScreenfuls
: rangeTuningParameters.leadingBufferScreenfuls;
return horizontalBuffer;
} }
CGRect asdk_CGRectExpandVertically(CGRect rect, CGFloat negativeMultiplier, CGFloat positiveMultiplier) { ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection,
CGFloat negativeDirectionHeight = negativeMultiplier * rect.size.height; ASRangeTuningParameters rangeTuningParameters)
CGFloat positiveDirectionHeight = positiveMultiplier * rect.size.height; {
CGFloat height = negativeDirectionHeight + rect.size.height + positiveDirectionHeight; ASDirectionalScreenfulBuffer verticalBuffer = {0, 0};
CGFloat originY = rect.origin.y - negativeDirectionHeight; BOOL movingDown = ASScrollDirectionContainsDown(scrollDirection);
return CGRectMake(rect.origin.x,
originY, verticalBuffer.positiveDirection = movingDown ? rangeTuningParameters.leadingBufferScreenfuls
rect.size.width, : rangeTuningParameters.trailingBufferScreenfuls;
height); verticalBuffer.negativeDirection = movingDown ? rangeTuningParameters.trailingBufferScreenfuls
: rangeTuningParameters.leadingBufferScreenfuls;
return verticalBuffer;
} }
CGRect CGRectExpandHorizontally(CGRect rect, ASDirectionalScreenfulBuffer buffer)
{
CGFloat negativeDirectionWidth = buffer.negativeDirection * rect.size.width;
CGFloat positiveDirectionWidth = buffer.positiveDirection * rect.size.width;
rect.size.width = negativeDirectionWidth + rect.size.width + positiveDirectionWidth;
rect.origin.x -= negativeDirectionWidth;
return rect;
}
CGRect CGRectExpandVertically(CGRect rect, ASDirectionalScreenfulBuffer buffer)
{
CGFloat negativeDirectionHeight = buffer.negativeDirection * rect.size.height;
CGFloat positiveDirectionHeight = buffer.positiveDirection * rect.size.height;
rect.size.height = negativeDirectionHeight + rect.size.height + positiveDirectionHeight;
rect.origin.y -= negativeDirectionHeight;
return rect;
}
CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningParameters tuningParameters,
ASScrollDirection scrollableDirections, ASScrollDirection scrollDirection)
{
// Can scroll horizontally - expand the range appropriately
if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) {
ASDirectionalScreenfulBuffer horizontalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection, tuningParameters);
rect = CGRectExpandHorizontally(rect, horizontalBuffer);
}
// Can scroll vertically - expand the range appropriately
if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) {
ASDirectionalScreenfulBuffer verticalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters);
rect = CGRectExpandVertically(rect, verticalBuffer);
}
return rect;
}

View File

@@ -10,8 +10,9 @@
@implementation UICollectionViewLayout (ASConvenience) @implementation UICollectionViewLayout (ASConvenience)
- (BOOL)asdk_isFlowLayout { - (BOOL)asdk_isFlowLayout
return [self isKindOfClass:UICollectionViewFlowLayout.class]; {
return [self isKindOfClass:[UICollectionViewFlowLayout class]];
} }
@end @end

View File

@@ -60,6 +60,7 @@ typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements. // These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
- (void)enterInterfaceState:(ASInterfaceState)interfaceState; - (void)enterInterfaceState:(ASInterfaceState)interfaceState;
- (void)exitInterfaceState:(ASInterfaceState)interfaceState; - (void)exitInterfaceState:(ASInterfaceState)interfaceState;
- (void)recursivelySetInterfaceState:(ASInterfaceState)interfaceState;
// These methods are recursive, and either union or remove the provided hierarchyState to all sub-elements. // These methods are recursive, and either union or remove the provided hierarchyState to all sub-elements.
- (void)enterHierarchyState:(ASHierarchyState)hierarchyState; - (void)enterHierarchyState:(ASHierarchyState)hierarchyState;

View File

@@ -250,7 +250,8 @@
_messageToViewOrLayer(setNeedsDisplay); _messageToViewOrLayer(setNeedsDisplay);
if ([ASDisplayNode shouldUseNewRenderingRange]) { if ([ASDisplayNode shouldUseNewRenderingRange]) {
if (_layer && !self.isSynchronous) { BOOL shouldDisplay = ((_interfaceState & ASInterfaceStateDisplay) == ASInterfaceStateDisplay);
if (_layer && !_flags.synchronous && shouldDisplay) {
[ASDisplayNode scheduleNodeForDisplay:self]; [ASDisplayNode scheduleNodeForDisplay:self];
} }
} }

View File

@@ -13,10 +13,15 @@
#import "ViewController.h" #import "ViewController.h"
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
[ASDisplayNode setShouldUseNewRenderingRange:YES];
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor]; self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]]; self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[ViewController alloc] init]];

View File

@@ -51,7 +51,7 @@
ASRangeTuningParameters rangeTuningParameters; ASRangeTuningParameters rangeTuningParameters;
rangeTuningParameters.leadingBufferScreenfuls = 1.0; rangeTuningParameters.leadingBufferScreenfuls = 1.0;
rangeTuningParameters.trailingBufferScreenfuls = 0.5; rangeTuningParameters.trailingBufferScreenfuls = 0.5;
[_tableNode.view setTuningParameters:rangeTuningParameters forRangeType:ASLayoutRangeTypeRender]; [_tableNode.view setTuningParameters:rangeTuningParameters forRangeType:ASLayoutRangeTypeDisplay];
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

View File

@@ -9,6 +9,9 @@
#import <AsyncDisplayKit/AsyncDisplayKit.h> #import <AsyncDisplayKit/AsyncDisplayKit.h>
@interface RandomCoreGraphicsNode : ASCellNode @interface RandomCoreGraphicsNode : ASCellNode
{
ASTextNode *_indexPathTextNode;
}
@property (nonatomic) NSIndexPath *indexPath; @property (nonatomic) NSIndexPath *indexPath;

View File

@@ -42,6 +42,37 @@
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
} }
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
_indexPathTextNode = [[ASTextNode alloc] init];
[self addSubnode:_indexPathTextNode];
return self;
}
- (void)setIndexPath:(NSIndexPath *)indexPath
{
_indexPath = indexPath;
_indexPathTextNode.attributedString = [[NSAttributedString alloc] initWithString:[indexPath description] attributes:nil];
}
//- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
//{
// ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[_indexPathTextNode]];
// stackSpec.flexGrow = YES;
// return stackSpec;
//}
- (void)layout
{
_indexPathTextNode.frame = self.bounds;
[super layout];
}
#if 0 #if 0
- (void)fetchData - (void)fetchData
{ {