Merge pull request #1605 from lappp9/override-callback-for-display-range

[ASDisplayNode] Added callbacks for entering and exiting fetch data and display ranges
This commit is contained in:
appleguy 2016-06-11 22:21:20 -07:00 committed by GitHub
commit a7128cd213
13 changed files with 140 additions and 25 deletions

View File

@ -214,9 +214,9 @@
// To be overriden by subclasses
}
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
CGRect cellFrame = CGRectZero;
if (_scrollView) {

View File

@ -173,9 +173,9 @@
}
#if ASRangeControllerLoggingEnabled
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
NSLog(@"%@ - visible: %d", self, isVisible);
}
#endif

View File

@ -242,6 +242,31 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (void)visibilityDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Called whenever the visiblity of the node changed.
*
* @discussion Subclasses may use this to monitor when they become visible.
*/
- (void)visibleStateDidChange:(BOOL)isVisible ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Called whenever the the node has entered or exited the display state.
*
* @discussion Subclasses may use this to monitor when a node should be rendering its content.
*
* @note This method can be called from any thread and should therefore be thread safe.
*/
- (void)displayStateDidChange:(BOOL)inDisplayState ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Called whenever the the node has entered or left the load state.
*
* @discussion Subclasses may use this to monitor data for a node should be loaded, either from a local or remote source.
*
* @note This method can be called from any thread and should therefore be thread safe.
*/
- (void)loadStateDidChange:(BOOL)inLoadState ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Called just before the view is added to a window.
*/

View File

@ -2044,6 +2044,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
ASDisplayNodeAssertMainThread();
}
#pragma mark Hierarchy State
- (void)willEnterHierarchy
{
ASDisplayNodeAssertMainThread();
@ -2064,7 +2066,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
if (![self supportsRangeManagedInterfaceState]) {
self.interfaceState = ASInterfaceStateNone;
} else {
// This case is important when tearing down hierarchies. We must deliver a visibilityDidChange:NO callback, as part our API guarantee that this method can be used for
// This case is important when tearing down hierarchies. We must deliver a visibileStateDidChange:NO callback, as part our API guarantee that this method can be used for
// things like data analytics about user content viewing. We cannot call the method in the dealloc as any incidental retain operations in client code would fail.
// Additionally, it may be that a Standard UIView which is containing us is moving between hierarchies, and we should not send the call if we will be re-added in the
// same runloop. Strategy: strong reference (might be the last!), wait one runloop, and confirm we are still outside the hierarchy (both layer-backed and view-backed).
@ -2083,6 +2085,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
}
}
#pragma mark Interface State
- (void)clearContents
{
// No-op if these haven't been created yet, as that guarantees they don't have contents that needs to be released.
@ -2131,7 +2135,22 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
- (void)visibilityDidChange:(BOOL)isVisible
{
// subclass override
// subclass override
}
- (void)visibleStateDidChange:(BOOL)isVisible
{
// subclass override
}
- (void)displayStateDidChange:(BOOL)inDisplayState
{
//subclass override
}
- (void)loadStateDidChange:(BOOL)inLoadState
{
//subclass override
}
/**
@ -2178,10 +2197,12 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
if (nowFetchData != wasFetchData) {
if (nowFetchData) {
[self fetchData];
[self loadStateDidChange:YES];
} else {
if ([self supportsRangeManagedInterfaceState]) {
[self clearFetchedData];
}
[self loadStateDidChange:NO];
}
}
@ -2225,6 +2246,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
}
}
}
[self displayStateDidChange:nowDisplay];
}
// Became visible or invisible. When range-managed, this represents literal visibility - at least one pixel
@ -2233,14 +2256,16 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
BOOL wasVisible = ASInterfaceStateIncludesVisible(oldState);
if (nowVisible != wasVisible) {
[self visibilityDidChange:nowVisible];
[self visibleStateDidChange:nowVisible];
[self visibilityDidChange:nowVisible]; //TODO: remove once this method has been deprecated
}
[self interfaceStateDidChange:newState fromState:oldState];
}
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState
{
// subclass hook
}
- (void)enterInterfaceState:(ASInterfaceState)interfaceState

View File

@ -158,9 +158,9 @@ NSString *const ASAnimatedImageDefaultRunLoopMode = NSDefaultRunLoopMode;
[self.animatedImage clearAnimatedImageCache];
}
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
ASDisplayNodeAssertMainThread();
if (isVisible) {

View File

@ -295,11 +295,11 @@ typedef void(^ASMultiplexImageLoadCompletionBlock)(UIImage *image, id imageIdent
}
}
/* visibilityDidChange in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary
/* visibileStateDidChange in ASNetworkImageNode has a very similar implementation. Changes here are likely necessary
in ASNetworkImageNode as well. */
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
if (_downloaderImplementsSetPriority) {
ASDN::MutexLocker l(_downloadIdentifierLock);

View File

@ -278,11 +278,11 @@ static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
}
}
/* visibilityDidChange in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary
/* visibileStateDidChange in ASMultiplexImageNode has a very similar implementation. Changes here are likely necessary
in ASMultiplexImageNode as well. */
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
if (_downloaderImplementsSetPriority) {
_lock.lock();

View File

@ -145,9 +145,9 @@
}
#if ASRangeControllerLoggingEnabled
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
NSLog(@"%@ - visible: %d", self, isVisible);
}
#endif

View File

@ -382,9 +382,9 @@ static NSString * const kStatus = @"status";
}
}
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
ASDN::MutexLocker l(_videoLock);

View File

@ -163,9 +163,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
}
}
- (void)visibilityDidChange:(BOOL)isVisible
- (void)visibleStateDidChange:(BOOL)isVisible
{
[super visibilityDidChange:isVisible];
[super visibleStateDidChange:isVisible];
ASDN::MutexLocker l(_videoPlayerLock);

View File

@ -95,7 +95,7 @@ static UIApplicationState __ApplicationState = UIApplicationStateActive;
{
_scrollDirection = scrollDirection;
// Perform update immediately, so that cells receive a visibilityDidChange: call before their first pixel is visible.
// Perform update immediately, so that cells receive a visibleStateDidChange: call before their first pixel is visible.
[self scheduleRangeUpdate];
}

View File

@ -86,6 +86,12 @@ for (ASDisplayNode *n in @[ nodes ]) {\
@property (atomic, copy) void (^willDeallocBlock)(ASTestDisplayNode *node);
@property (atomic, copy) CGSize(^calculateSizeBlock)(ASTestDisplayNode *node, CGSize size);
@property (atomic) BOOL hasFetchedData;
@property (atomic) BOOL displayRangeStateChangedToYES;
@property (atomic) BOOL displayRangeStateChangedToNO;
@property (atomic) BOOL loadStateChangedToYES;
@property (atomic) BOOL loadStateChangedToNO;
@end
@interface ASTestResponderNode : ASTestDisplayNode
@ -110,6 +116,28 @@ for (ASDisplayNode *n in @[ nodes ]) {\
self.hasFetchedData = NO;
}
- (void)displayStateDidChange:(BOOL)inDisplayState
{
[super displayStateDidChange:inDisplayState];
if (inDisplayState) {
self.displayRangeStateChangedToYES = YES;
} else {
self.displayRangeStateChangedToNO = YES;
}
}
- (void)loadStateDidChange:(BOOL)inLoadState
{
[super loadStateDidChange:inLoadState];
if (inLoadState) {
self.loadStateChangedToYES = YES;
} else {
self.loadStateChangedToNO = YES;
}
}
- (void)dealloc
{
if (_willDeallocBlock) {
@ -1878,4 +1906,42 @@ static bool stringContainsPointer(NSString *description, const void *p) {
XCTAssert(node.bounds.size.height == 8, @"Wrong ASDisplayNode.bounds.size.height");
}
- (void)testDidEnterDisplayIsCalledWhenNodesEnterDisplayRange
{
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
[node recursivelySetInterfaceState:ASInterfaceStateDisplay];
XCTAssert([node displayRangeStateChangedToYES]);
}
- (void)testDidExitDisplayIsCalledWhenNodesExitDisplayRange
{
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
[node recursivelySetInterfaceState:ASInterfaceStateDisplay];
[node recursivelySetInterfaceState:ASInterfaceStateFetchData];
XCTAssert([node displayRangeStateChangedToNO]);
}
- (void)testDidEnterFetchDataIsCalledWhenNodesEnterFetchDataRange
{
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
[node recursivelySetInterfaceState:ASInterfaceStateFetchData];
XCTAssert([node loadStateChangedToYES]);
}
- (void)testDidExitFetchDataIsCalledWhenNodesExitFetchDataRange
{
ASTestDisplayNode *node = [[ASTestDisplayNode alloc] init];
[node recursivelySetInterfaceState:ASInterfaceStateFetchData];
[node recursivelySetInterfaceState:ASInterfaceStateDisplay];
XCTAssert([node loadStateChangedToNO]);
}
@end

View File

@ -223,12 +223,11 @@
}];
_videoNode.playerNode.layer.frame = CGRectZero;
[_videoNode visibilityDidChange:YES];
[_videoNode visibleStateDidChange:YES];
XCTAssertTrue(_videoNode.shouldBePlaying);
}
- (void)testVideoShouldPauseWhenItLeavesVisibleButShouldKnowPlayingShouldRestartLater
{
_videoNode.asset = _firstAsset;