Range controller registers to rendering engine and extern to full range if needed

This commit is contained in:
Huy Nguyen
2016-02-01 23:42:13 -08:00
parent 1145b6e40e
commit 69e674c1c8
8 changed files with 143 additions and 76 deletions

View File

@@ -29,6 +29,7 @@
#import "ASCellNode.h"
NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
@interface _ASDisplayNodePosition : NSObject
@@ -279,6 +280,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
for (ASDisplayNode *node in displayingNodes) {
[node __recursivelyTriggerDisplayAndBlock:NO];
}
[[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification
object:nil];
});
}
}

View File

@@ -11,8 +11,7 @@
#include <vector>
@interface ASAbstractLayoutController () {
std::vector<ASRangeTuningParameters> _tuningParameters;
std::vector<ASRangeTuningParameters> _minimumTuningParameters;
std::vector<std::vector<ASRangeTuningParameters>> _tuningParameters;
CGSize _viewportSize;
}
@end
@@ -25,34 +24,34 @@
return nil;
}
_tuningParameters = std::vector<ASRangeTuningParameters>(ASLayoutRangeTypeCount);
_tuningParameters[ASLayoutRangeTypeVisible] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters[ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.5,
.trailingBufferScreenfuls = 0.75
};
_tuningParameters[ASLayoutRangeTypeFetchData] = {
.leadingBufferScreenfuls = 3,
.trailingBufferScreenfuls = 2
};
_tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount));
_minimumTuningParameters = std::vector<ASRangeTuningParameters>(ASLayoutRangeTypeCount);
_minimumTuningParameters[ASLayoutRangeTypeVisible] = {
_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeVisible] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_minimumTuningParameters[ASLayoutRangeTypeDisplay] = {
_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0.25,
.trailingBufferScreenfuls = 0.25
};
_minimumTuningParameters[ASLayoutRangeTypeFetchData] = {
_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeFetchData] = {
.leadingBufferScreenfuls = 1,
.trailingBufferScreenfuls = 1
};
_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeVisible] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.5,
.trailingBufferScreenfuls = 0.75
};
_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeFetchData] = {
.leadingBufferScreenfuls = 3,
.trailingBufferScreenfuls = 2
};
return self;
}
@@ -60,33 +59,28 @@
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters");
return _tuningParameters[rangeType];
return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _tuningParameters.size(), @"Requesting a range that is OOB for the configured tuning parameters");
ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible, @"Must not set Visible range tuning parameters (always 0, 0)");
_tuningParameters[rangeType] = tuningParameters;
return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
}
- (ASRangeTuningParameters)minimumTuningParametersForRangeType:(ASLayoutRangeType)rangeType
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _minimumTuningParameters.size(), @"Requesting a range that is OOB for the configured minimum tuning parameters");
return _minimumTuningParameters[rangeType];
ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(),
@"Requesting a range that is OOB for the configured tuning parameters");
return _tuningParameters[rangeMode][rangeType];
}
- (void)setMinimumTuningParameters:(ASRangeTuningParameters)minimumTuningParameters forRangeType:(ASLayoutRangeType)rangeType
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeType < _minimumTuningParameters.size(), @"Requesting a range that is OOB for the configured minimum tuning parameters");
ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible, @"Must not set Visible range minimum tuning parameters (always 0, 0)");
_minimumTuningParameters[rangeType] = minimumTuningParameters;
}
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType isFullRange:(BOOL)isFullRange
{
return isFullRange ? [self tuningParametersForRangeType:rangeType] : [self minimumTuningParametersForRangeType:rangeType];
ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(),
@"Setting a range that is OOB for the configured tuning parameters");
ASDisplayNodeAssert(rangeType != ASLayoutRangeTypeVisible,
@"Must not set Visible range minimum tuning parameters (always 0, 0)");
_tuningParameters[rangeMode][rangeType] = tuningParameters;
}
#pragma mark - Abstract Index Path Range Support
@@ -98,7 +92,7 @@
return NO;
}
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssertNotSupported();
return nil;

View File

@@ -66,9 +66,9 @@ typedef struct ASRangeGeometry ASRangeGeometry;
return self;
}
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType isFullRange:shouldUseFullRange];
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
ASRangeGeometry rangeGeometry = [self rangeGeometryWithScrollDirection:scrollDirection tuningParameters:tuningParameters];
_updateRangeBoundsIndexedByRangeType[rangeType] = rangeGeometry.updateBounds;
return [self indexPathsForItemsWithinRangeBounds:rangeGeometry.rangeBounds];
@@ -133,9 +133,9 @@ typedef struct ASRangeGeometry ASRangeGeometry;
@implementation ASCollectionViewLayoutControllerBeta
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType isFullRange:shouldUseFullRange];
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
CGRect rangeBounds = [self rangeBoundsWithScrollDirection:scrollDirection rangeTuningParameters:tuningParameters];
return [self indexPathsForItemsWithinRangeBounds:rangeBounds];
}

View File

@@ -74,7 +74,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
* IndexPath array for the element in the working range.
*/
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
CGFloat viewportScreenMetric;
ASScrollDirection leadingDirection;
@@ -92,7 +92,7 @@ static const CGFloat kASFlowLayoutControllerRefreshingThreshold = 0.3;
leadingDirection = ASScrollDirectionUp;
}
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeType:rangeType isFullRange:shouldUseFullRange];
ASRangeTuningParameters tuningParameters = [self tuningParametersForRangeMode:rangeMode rangeType:rangeType];
CGFloat backScreens = scrollDirection == leadingDirection ? tuningParameters.leadingBufferScreenfuls : tuningParameters.trailingBufferScreenfuls;
CGFloat frontScreens = scrollDirection == leadingDirection ? tuningParameters.trailingBufferScreenfuls : tuningParameters.leadingBufferScreenfuls;

View File

@@ -16,6 +16,12 @@ NS_ASSUME_NONNULL_BEGIN
@class ASCellNode;
typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) {
ASLayoutRangeModeMinimum = 0,
ASLayoutRangeModeFull,
ASLayoutRangeModeCount
};
typedef struct {
CGFloat leadingBufferScreenfuls;
CGFloat trailingBufferScreenfuls;
@@ -24,23 +30,30 @@ typedef struct {
@protocol ASLayoutController <NSObject>
/**
* Tuning parameters for the range.
* Tuning parameters for the range type in full mode. This method is deprecated.
* Instead, use -setTuningParameters:forRangeMode:rangeType:
*
* @see setTuningParameters:forRangeMode:rangeType:
*/
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType;
/**
* Get tuning parameters for the range type in full mode. This method is deprecated.
* Instead, use -tuningParametersForRangeMode:rangeType:
*
* @see tuningParametersForRangeMode:rangeType:
*/
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType ASDISPLAYNODE_DEPRECATED;
- (void)setMinimumTuningParameters:(ASRangeTuningParameters)minimumTuningParameters forRangeType:(ASLayoutRangeType)rangeType;
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)minimumTuningParametersForRangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType isFullRange:(BOOL)isFullRange;
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
// FIXME: This method can be removed once ASRangeControllerBeta becomes the main version.
// TODO: Now that it is the main version, can we remove this now?
- (BOOL)shouldUpdateForVisibleIndexPaths:(NSArray<NSIndexPath *> *)indexPaths rangeType:(ASLayoutRangeType)rangeType;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeType:(ASLayoutRangeType)rangeType shouldUseFullRange:(BOOL)shouldUseFullRange;
- (NSSet *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
@optional

View File

@@ -153,7 +153,9 @@
id<ASRangeHandler> rangeHandler = _rangeTypeHandlers[rangeKey];
if (!_rangeIsValid || [_layoutController shouldUpdateForVisibleIndexPaths:visibleNodePaths rangeType:rangeType]) {
NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:rangeType shouldUseFullRange:YES];
NSSet *indexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
rangeMode:ASLayoutRangeModeFull
rangeType:rangeType];
// Notify to remove indexpaths that are leftover that are not visible or included in the _layoutController calculated paths
NSMutableSet *removedIndexPaths = _rangeIsValid ? [_rangeTypeIndexPaths[rangeKey] mutableCopy] : [NSMutableSet set];

View File

@@ -10,6 +10,7 @@
#import "ASAssert.h"
#import "ASDisplayNodeExtras.h"
#import "ASDisplayNodeInternal.h"
#import "ASMultiDimensionalArrayUtils.h"
#import "ASRangeHandlerVisible.h"
#import "ASRangeHandlerRender.h"
@@ -17,12 +18,6 @@
#import "ASInternalHelpers.h"
#import "ASDisplayNode+FrameworkPrivate.h"
typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) {
ASRangeTypeUsedNone,
ASRangeTypeUsedMinimum,
ASRangeTypeUsedFull,
};
@interface ASRangeControllerBeta ()
{
BOOL _rangeIsValid;
@@ -30,7 +25,8 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) {
BOOL _layoutControllerImplementsSetVisibleIndexPaths;
ASScrollDirection _scrollDirection;
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
ASRangeTypeUsed _rangeTypeUsed;
ASLayoutRangeMode _currentRangeMode;
BOOL _didRegisterForNotifications;
}
@end
@@ -44,13 +40,41 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) {
}
_rangeIsValid = YES;
_rangeTypeUsed = ASRangeTypeUsedNone;
_currentRangeMode = ASLayoutRangeModeCount;
return self;
}
- (void)dealloc
{
if (_didRegisterForNotifications) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
}
#pragma mark - Core visible node range managment API
+ (ASLayoutRangeMode)rangeModeForInterfaceState:(ASInterfaceState)interfaceState
scrollDirection:(ASScrollDirection)scrollDirection
currentRangeMode:(ASLayoutRangeMode)currentRangeMode
{
// If we used full mode, don't switch to minimum mode. That will destroy all the hard work done before.
if (currentRangeMode == ASLayoutRangeModeFull) {
return ASLayoutRangeModeFull;
}
BOOL isVisible = (ASInterfaceStateIncludesVisible(interfaceState));
BOOL isScrolling = (scrollDirection != ASScrollDirectionNone);
BOOL isUsingMinimumRangeMode = (currentRangeMode == ASLayoutRangeModeMinimum);
// If we are already visible and scrolling, get busy! Better get started on preloading before the user scrolls more...
// If we are already visible and finished displaying minimum mode, extend to full mode
if (isVisible && (isScrolling || isUsingMinimumRangeMode)) {
return ASLayoutRangeModeFull;
}
return ASLayoutRangeModeMinimum;
}
- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
{
_scrollDirection = scrollDirection;
@@ -117,25 +141,25 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) {
NSMutableOrderedSet<NSIndexPath *> *allIndexPaths = [[NSMutableOrderedSet alloc] initWithSet:visibleIndexPaths];
ASInterfaceState selfInterfaceState = [_dataSource interfaceStateForRangeController:self];
BOOL selfIsVisible = (ASInterfaceStateIncludesVisible(selfInterfaceState));
BOOL selfIsScrolling = (_scrollDirection != ASScrollDirectionNone);
BOOL didUseMinimumRange = (_rangeTypeUsed == ASRangeTypeUsedMinimum);
BOOL didUseFullRange = (_rangeTypeUsed == ASRangeTypeUsedFull);
// If we are already visible and scrolling, get busy! Better get started on preloading before the user scrolls more...
// If we are already visible and did finish displaying minimum range, extend to full range
// If we used full range, don't switch to minimum range now. That will destroy all the hard work done before.
BOOL useFullRange = ((selfIsVisible && (selfIsScrolling || didUseMinimumRange)) || didUseFullRange);
NSLog(@"%@ range: %@", useFullRange ? @"Full" : @"Minimum", [((ASCollectionView *)_delegate).asyncDelegate description]);
ASLayoutRangeMode rangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:selfInterfaceState
scrollDirection:_scrollDirection
currentRangeMode:_currentRangeMode];
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeFetchData shouldUseFullRange:useFullRange];
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
rangeMode:rangeMode
rangeType:ASLayoutRangeTypeFetchData];
ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeType:ASLayoutRangeTypeDisplay isFullRange:useFullRange];
ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeType:ASLayoutRangeTypeFetchData isFullRange:useFullRange];
ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeMode:rangeMode
rangeType:ASLayoutRangeTypeDisplay];
ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeMode:rangeMode
rangeType:ASLayoutRangeTypeFetchData];
if (parametersDisplay.leadingBufferScreenfuls == parametersFetchData.leadingBufferScreenfuls &&
parametersDisplay.trailingBufferScreenfuls == parametersFetchData.trailingBufferScreenfuls) {
displayIndexPaths = fetchDataIndexPaths;
} else {
displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection rangeType:ASLayoutRangeTypeDisplay shouldUseFullRange:useFullRange];
displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
rangeMode:rangeMode
rangeType:ASLayoutRangeTypeDisplay];
}
// Typically the fetchDataIndexPaths will be the largest, and be a superset of the others, though it may be disjoint.
@@ -151,12 +175,14 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) {
NSSet<NSIndexPath *> *allCurrentIndexPaths = [[allIndexPaths set] copy];
[allIndexPaths unionSet:_allPreviousIndexPaths];
_allPreviousIndexPaths = allCurrentIndexPaths;
_rangeTypeUsed = useFullRange ? ASRangeTypeUsedFull : ASRangeTypeUsedMinimum;
_currentRangeMode = rangeMode;
if (!_rangeIsValid) {
[allIndexPaths addObjectsFromArray:ASIndexPathsForMultidimensionalArray(allNodes)];
}
[self registerForNotificationsIfNeeded];
// This array is only used if logging is enabled.
NSMutableArray<NSIndexPath *> *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array] : nil);
@@ -240,6 +266,33 @@ typedef NS_ENUM(NSUInteger, ASRangeTypeUsed) {
#endif
}
#pragma mark - Notification observers
- (void)registerForNotificationsIfNeeded
{
if (!_didRegisterForNotifications) {
BOOL selfInterfaceState = [_dataSource interfaceStateForRangeController:self];
ASLayoutRangeMode nextRangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:selfInterfaceState
scrollDirection:_scrollDirection
currentRangeMode:_currentRangeMode];
if (_currentRangeMode != nextRangeMode) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(scheduledNodesDidDisplay)
name:ASRenderingEngineDidDisplayScheduledNodesNotification
object:nil];
_didRegisterForNotifications = YES;
}
}
}
- (void)scheduledNodesDidDisplay
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
_didRegisterForNotifications = NO;
[self scheduleRangeUpdate];
}
#pragma mark - Cell node view handling
- (void)configureContentView:(UIView *)contentView forCellNode:(ASCellNode *)node

View File

@@ -37,6 +37,8 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
@class _ASPendingState;
@class _ASDisplayNodePosition;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
// Allow 2^n increments of begin disabling hierarchy notifications
#define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4