Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion AsyncDisplayKit/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

NSInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification = @"ASRenderingEngineDidDisplayScheduledNodes";
NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp";

@interface _ASDisplayNodePosition : NSObject

Expand Down Expand Up @@ -276,12 +277,14 @@ + (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node
ASDN::MutexLocker l(displaySchedulerLock);
displayScheduled = NO;
NSSet *displayingNodes = [nodesToDisplay copy];
CFAbsoluteTime timestamp = CFAbsoluteTimeGetCurrent();
nodesToDisplay = nil;
for (ASDisplayNode *node in displayingNodes) {
[node __recursivelyTriggerDisplayAndBlock:NO];
}
[[NSNotificationCenter defaultCenter] postNotificationName:ASRenderingEngineDidDisplayScheduledNodesNotification
object:nil];
object:displayingNodes
userInfo:@{ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp: [NSNumber numberWithDouble:timestamp]}];
});
}
}
Expand Down
13 changes: 9 additions & 4 deletions AsyncDisplayKit/ASPagerNode.m
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,16 @@ - (void)didLoad
// our view is only horizontally scrollable. This causes UICollectionViewFlowLayout to log a warning.
// From here we cannot disable this directly (UIViewController's automaticallyAdjustsScrollViewInsets).
cv.zeroContentInsets = YES;

ASRangeTuningParameters minimumRenderParams = { .leadingBufferScreenfuls = 0.0, .trailingBufferScreenfuls = 0.0 };
ASRangeTuningParameters minimumPreloadParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
[self setTuningParameters:minimumRenderParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeDisplay];
[self setTuningParameters:minimumPreloadParams forRangeMode:ASLayoutRangeModeMinimum rangeType:ASLayoutRangeTypeFetchData];

ASRangeTuningParameters preloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 };
ASRangeTuningParameters renderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
[self setTuningParameters:preloadParams forRangeType:ASLayoutRangeTypeFetchData];
[self setTuningParameters:renderParams forRangeType:ASLayoutRangeTypeDisplay];
ASRangeTuningParameters fullRenderParams = { .leadingBufferScreenfuls = 1.0, .trailingBufferScreenfuls = 1.0 };
ASRangeTuningParameters fullPreloadParams = { .leadingBufferScreenfuls = 2.0, .trailingBufferScreenfuls = 2.0 };
[self setTuningParameters:fullRenderParams forRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeDisplay];
[self setTuningParameters:fullPreloadParams forRangeMode:ASLayoutRangeModeFull rangeType:ASLayoutRangeTypeFetchData];
}

#pragma mark - Helpers
Expand Down
17 changes: 7 additions & 10 deletions AsyncDisplayKit/Details/ASAbstractLayoutController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
#import "ASAssert.h"
#include <vector>

extern ASRangeTuningParameters const ASRangeTuningParametersZero = {};

extern BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs)
{
return lhs.leadingBufferScreenfuls == rhs.leadingBufferScreenfuls && lhs.trailingBufferScreenfuls == rhs.trailingBufferScreenfuls;
}

@interface ASAbstractLayoutController () {
std::vector<std::vector<ASRangeTuningParameters>> _tuningParameters;
CGSize _viewportSize;
Expand All @@ -26,10 +33,6 @@ - (instancetype)init

_tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount));

_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeVisible] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0.25,
.trailingBufferScreenfuls = 0.25
Expand All @@ -39,10 +42,6 @@ - (instancetype)init
.trailingBufferScreenfuls = 1
};

_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeVisible] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
_tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.5,
.trailingBufferScreenfuls = 0.75
Expand Down Expand Up @@ -78,8 +77,6 @@ - (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMo
{
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;
}

Expand Down
4 changes: 4 additions & 0 deletions AsyncDisplayKit/Details/ASLayoutController.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ typedef struct {
CGFloat trailingBufferScreenfuls;
} ASRangeTuningParameters;

FOUNDATION_EXPORT ASRangeTuningParameters const ASRangeTuningParametersZero;

FOUNDATION_EXPORT BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs);

@protocol ASLayoutController <NSObject>

- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
Expand Down
27 changes: 17 additions & 10 deletions AsyncDisplayKit/Details/ASLayoutRangeType.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,30 @@

#import <Foundation/Foundation.h>

/// Each mode has a complete set of tuning parameters for range types.
/// Depends on some conditions (including interface state and direction of the scroll view, state of rendering engine, etc),
/// a range controller can choose which mode it should use at a given time.
/**
* Each mode has a complete set of tuning parameters for range types.
* Depending on some conditions (including interface state and direction of the scroll view, state of rendering engine, etc),
* a range controller can choose which mode it should use at a given time.
*/
typedef NS_ENUM(NSUInteger, ASLayoutRangeMode) {
/// Minimum mode is used when a range controller should limit the amount of work it performs.
/// Thus, less views/layers are created and less data is fetched.
/// Range controller can automatically switch to full mode when conditions changed.
/**
* Minimum mode is used when a range controller should limit the amount of work it performs.
* Thus, fewer views/layers are created and less data is fetched, saving system resources.
* Range controller can automatically switch to full mode when conditions change.
*/
ASLayoutRangeModeMinimum = 0,
/// Normal/Full mode that a range controller uses to provide the best experience for end users.
/// This mode is usually used for an active scroll view.
/// A range controller under this requires more resources compare to minimum mode.
/**
* Normal/Full mode that a range controller uses to provide the best experience for end users.
* This mode is usually used for an active scroll view.
* A range controller under this requires more resources compare to minimum mode.
*/
ASLayoutRangeModeFull,
ASLayoutRangeModeCount
};

#define ASLayoutRangeModeInvalid ASLayoutRangeModeCount

typedef NS_ENUM(NSInteger, ASLayoutRangeType) {
ASLayoutRangeTypeVisible = 0,
ASLayoutRangeTypeDisplay,
ASLayoutRangeTypeFetchData,
ASLayoutRangeTypeCount
Expand Down
1 change: 0 additions & 1 deletion AsyncDisplayKit/Details/ASRangeController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ - (instancetype)init
_rangeIsValid = YES;
_rangeTypeIndexPaths = [NSMutableDictionary dictionary];
_rangeTypeHandlers = @{
@(ASLayoutRangeTypeVisible) : [[ASRangeHandlerVisible alloc] init],
@(ASLayoutRangeTypeDisplay) : [[ASRangeHandlerRender alloc] init],
@(ASLayoutRangeTypeFetchData): [[ASRangeHandlerPreload alloc] init],
};
Expand Down
78 changes: 43 additions & 35 deletions AsyncDisplayKit/Details/ASRangeControllerBeta.mm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ @interface ASRangeControllerBeta ()
NSSet<NSIndexPath *> *_allPreviousIndexPaths;
ASLayoutRangeMode _currentRangeMode;
BOOL _didRegisterForNotifications;
CFAbsoluteTime _pendingDisplayNodesTimestamp;
}

@end
Expand All @@ -40,39 +41,30 @@ - (instancetype)init
}

_rangeIsValid = YES;
_currentRangeMode = ASLayoutRangeModeCount;
_currentRangeMode = ASLayoutRangeModeInvalid;

return self;
}

- (void)dealloc
{
if (_didRegisterForNotifications) {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
}
}

#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;
BOOL isFirstRangeUpdate = (currentRangeMode == ASLayoutRangeModeInvalid);
if (!isVisible || isFirstRangeUpdate) {
return ASLayoutRangeModeMinimum;
}

return ASLayoutRangeModeMinimum;
return ASLayoutRangeModeFull;
}

- (void)visibleNodeIndexPathsDidChangeWithScrollDirection:(ASScrollDirection)scrollDirection
Expand Down Expand Up @@ -145,16 +137,21 @@ - (void)_updateVisibleNodeIndexPaths
scrollDirection:_scrollDirection
currentRangeMode:_currentRangeMode];

fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
rangeMode:rangeMode
rangeType:ASLayoutRangeTypeFetchData];
ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeMode:rangeMode
rangeType:ASLayoutRangeTypeFetchData];
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersFetchData, ASRangeTuningParametersZero)) {
fetchDataIndexPaths = visibleIndexPaths;
} else {
fetchDataIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
rangeMode:rangeMode
rangeType:ASLayoutRangeTypeFetchData];
}

ASRangeTuningParameters parametersDisplay = [_layoutController tuningParametersForRangeMode:rangeMode
rangeType:ASLayoutRangeTypeDisplay];
ASRangeTuningParameters parametersFetchData = [_layoutController tuningParametersForRangeMode:rangeMode
rangeType:ASLayoutRangeTypeFetchData];
if (parametersDisplay.leadingBufferScreenfuls == parametersFetchData.leadingBufferScreenfuls &&
parametersDisplay.trailingBufferScreenfuls == parametersFetchData.trailingBufferScreenfuls) {
if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, ASRangeTuningParametersZero)) {
displayIndexPaths = visibleIndexPaths;
} else if (ASRangeTuningParametersEqualToRangeTuningParameters(parametersDisplay, parametersFetchData)) {
displayIndexPaths = fetchDataIndexPaths;
} else {
displayIndexPaths = [_layoutController indexPathsForScrolling:_scrollDirection
Expand All @@ -181,10 +178,13 @@ - (void)_updateVisibleNodeIndexPaths
[allIndexPaths addObjectsFromArray:ASIndexPathsForMultidimensionalArray(allNodes)];
}

[self registerForNotificationsIfNeeded];
// TODO Don't register for notifications if this range update doesn't cause any node to enter rendering pipeline.
// This can be done once there is an API to observe to (or be notified upon) interface state changes or pipeline enterings
[self registerForNotificationsForInterfaceStateIfNeeded:selfInterfaceState];

// This array is only used if logging is enabled.
#if RangeControllerLoggingEnabled
NSMutableArray<NSIndexPath *> *modifiedIndexPaths = (RangeControllerLoggingEnabled ? [NSMutableArray array] : nil);
#endif

for (NSIndexPath *indexPath in allIndexPaths) {
// Before a node / indexPath is exposed to ASRangeController, ASDataController should have already measured it.
Expand Down Expand Up @@ -234,13 +234,19 @@ - (void)_updateVisibleNodeIndexPaths
ASDisplayNodeAssert(node.hierarchyState & ASHierarchyStateRangeManaged, @"All nodes reaching this point should be range-managed, or interfaceState may be incorrectly reset.");
// Skip the many method calls of the recursive operation if the top level cell node already has the right interfaceState.
if (node.interfaceState != interfaceState) {
#if RangeControllerLoggingEnabled
[modifiedIndexPaths addObject:indexPath];
#endif
[node recursivelySetInterfaceState:interfaceState];
}
}
}
}


if (_didRegisterForNotifications) {
_pendingDisplayNodesTimestamp = CFAbsoluteTimeGetCurrent();
}

_rangeIsValid = YES;
_queuedRangeUpdate = NO;

Expand Down Expand Up @@ -268,29 +274,31 @@ - (void)_updateVisibleNodeIndexPaths

#pragma mark - Notification observers

- (void)registerForNotificationsIfNeeded
- (void)registerForNotificationsForInterfaceStateIfNeeded:(ASInterfaceState)interfaceState
{
if (!_didRegisterForNotifications) {
BOOL selfInterfaceState = [_dataSource interfaceStateForRangeController:self];
ASLayoutRangeMode nextRangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:selfInterfaceState
scrollDirection:_scrollDirection
ASLayoutRangeMode nextRangeMode = [ASRangeControllerBeta rangeModeForInterfaceState:interfaceState
currentRangeMode:_currentRangeMode];
if (_currentRangeMode != nextRangeMode) {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(scheduledNodesDidDisplay)
selector:@selector(scheduledNodesDidDisplay:)
name:ASRenderingEngineDidDisplayScheduledNodesNotification
object:nil];
_didRegisterForNotifications = YES;
}
}
}

- (void)scheduledNodesDidDisplay
- (void)scheduledNodesDidDisplay:(NSNotification *)notification
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
_didRegisterForNotifications = NO;

[self scheduleRangeUpdate];
CFAbsoluteTime notificationTimestamp = ((NSNumber *)[notification.userInfo objectForKey:ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp]).doubleValue;
if (_pendingDisplayNodesTimestamp < notificationTimestamp) {
// The rendering engine has processed all the nodes this range controller scheduled. Let's schedule a range update
[[NSNotificationCenter defaultCenter] removeObserver:self name:ASRenderingEngineDidDisplayScheduledNodesNotification object:nil];
_didRegisterForNotifications = NO;

[self scheduleRangeUpdate];
}
}

#pragma mark - Cell node view handling
Expand Down
1 change: 1 addition & 0 deletions AsyncDisplayKit/Private/ASDisplayNodeInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
@class _ASDisplayNodePosition;

FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp;

// Allow 2^n increments of begin disabling hierarchy notifications
#define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4
Expand Down