Skip to content

Commit

Permalink
Adds support for having multiple interface state delegates. (#979)
Browse files Browse the repository at this point in the history
* Adds support for having multiple interface state delegates.

Hopefully in a performant way.

* Switch to respondsToSelector for int del instead of separate object

* Add CHANGELOG

* Make ASDisplayNode+InterfaceState.h public

* Huy's comments

* Don't even bother removing since it's a weak hash table.
  • Loading branch information
garrettmoon committed Jun 27, 2018
1 parent f0dac14 commit a4f78ad
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 115 deletions.
4 changes: 4 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B361CB57A5A001D4E68 /* ASPINRemoteImageDownloader.m */; };
68355B401CB57A69001D4E68 /* ASImageContainerProtocolCategories.m in Sources */ = {isa = PBXBuildFile; fileRef = 68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */; };
68355B411CB57A6C001D4E68 /* ASImageContainerProtocolCategories.h in Headers */ = {isa = PBXBuildFile; fileRef = 68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */; settings = {ATTRIBUTES = (Public, ); }; };
683F563720E409D700CEB7A3 /* ASDisplayNode+InterfaceState.h in Headers */ = {isa = PBXBuildFile; fileRef = 683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */; settings = {ATTRIBUTES = (Public, ); }; };
68AF37DB1CBEF4D80077BF76 /* ASImageNode+AnimatedImagePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; };
68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
68B8A4E21CBDB958007E4543 /* ASWeakProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B8A4DF1CBDB958007E4543 /* ASWeakProxy.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -667,6 +668,7 @@
68355B371CB57A5A001D4E68 /* ASImageContainerProtocolCategories.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASImageContainerProtocolCategories.h; sourceTree = "<group>"; };
68355B381CB57A5A001D4E68 /* ASImageContainerProtocolCategories.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASImageContainerProtocolCategories.m; sourceTree = "<group>"; };
68355B391CB57A5A001D4E68 /* ASPINRemoteImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASPINRemoteImageDownloader.h; sourceTree = "<group>"; };
683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+InterfaceState.h"; sourceTree = "<group>"; };
68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Beta.h"; sourceTree = "<group>"; };
68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASImageNode+AnimatedImagePrivate.h"; sourceTree = "<group>"; };
68B8A4DF1CBDB958007E4543 /* ASWeakProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASWeakProxy.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1156,6 +1158,7 @@
68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */,
CC034A071E60BEB400626263 /* ASDisplayNode+Convenience.h */,
CC034A081E60BEB400626263 /* ASDisplayNode+Convenience.m */,
683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */,
69BCE3D71EC6513B007DCCAD /* ASDisplayNode+Layout.mm */,
058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */,
90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */,
Expand Down Expand Up @@ -1915,6 +1918,7 @@
CC034A091E60BEB400626263 /* ASDisplayNode+Convenience.h in Headers */,
254C6B7E1BF94DF4003EC431 /* ASTextKitTailTruncater.h in Headers */,
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
683F563720E409D700CEB7A3 /* ASDisplayNode+InterfaceState.h in Headers */,
68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */,
CCCCCCE11EC3EF060087FE10 /* ASTextUtilities.h in Headers */,
B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */,
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## master
* Add your own contributions to the next release on the line below this with your name.
- [ASDisplayNode] Adds support for multiple interface state delegates. [Garrett Moon](https://github.com/garrettmoon) [#979](https://github.com/TextureGroup/Texture/pull/979)
- [ASDataController] Add capability to renew supplementary views (update map) when size change from zero to non-zero.[Max Wang](https://github.com/wsdwsd0829) [#842](https://github.com/TextureGroup/Texture/pull/842)
- Make `ASPerformMainThreadDeallocation` visible in C. [Adlai Holler](https://github.com/Adlai-Holler)
- Add snapshot test for astextnode2. [Max Wang](https://github.com/wsdwsd0829) [#935](https://github.com/TextureGroup/Texture/pull/935)
Expand Down
124 changes: 124 additions & 0 deletions Source/ASDisplayNode+InterfaceState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// ASDisplayNode+InterfaceState.h
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//

#import <Foundation/Foundation.h>

/**
* Interface state is available on ASDisplayNode and ASViewController, and
* allows checking whether a node is in an interface situation where it is prudent to trigger certain
* actions: measurement, data loading, display, and visibility (the latter for animations or other onscreen-only effects).
*
* The defualt state, ASInterfaceStateNone, means that the element is not predicted to be onscreen soon and
* preloading should not be performed. Swift: use [] for the default behavior.
*/
typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
{
/** The element is not predicted to be onscreen soon and preloading should not be performed */
ASInterfaceStateNone = 0,
/** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */
ASInterfaceStateMeasureLayout = 1 << 0,
/** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */
ASInterfaceStatePreload = 1 << 1,
/** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */
ASInterfaceStateDisplay = 1 << 2,
/** The element is physically onscreen by at least 1 pixel.
In practice, all other bit fields should also be set when this flag is set. */
ASInterfaceStateVisible = 1 << 3,

/**
* The node is not contained in a cell but it is in a window.
*
* Currently we only set `interfaceState` to other values for
* nodes contained in table views or collection views.
*/
ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStatePreload | ASInterfaceStateDisplay | ASInterfaceStateVisible,
};

@protocol ASInterfaceStateDelegate <NSObject>
@optional

/**
* @abstract Called whenever any bit in the ASInterfaceState bitfield is changed.
* @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more.
* @see ASInterfaceState
*/
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState;

/**
* @abstract Called whenever the node becomes visible.
* @discussion Subclasses may use this to monitor when they become visible.
* @note This method is guaranteed to be called on main.
*/
- (void)didEnterVisibleState;

/**
* @abstract Called whenever the node is no longer visible.
* @discussion Subclasses may use this to monitor when they are no longer visible.
* @note This method is guaranteed to be called on main.
*/
- (void)didExitVisibleState;

/**
* @abstract Called whenever the the node has entered the display state.
* @discussion Subclasses may use this to monitor when a node should be rendering its content.
* @note This method is guaranteed to be called on main.
*/
- (void)didEnterDisplayState;

/**
* @abstract Called whenever the the node has exited the display state.
* @discussion Subclasses may use this to monitor when a node should no longer be rendering its content.
* @note This method is guaranteed to be called on main.
*/
- (void)didExitDisplayState;

/**
* @abstract Called whenever the the node has entered the preload state.
* @discussion Subclasses may use this to monitor data for a node should be preloaded, either from a local or remote source.
* @note This method is guaranteed to be called on main.
*/
- (void)didEnterPreloadState;

/**
* @abstract Called whenever the the node has exited the preload state.
* @discussion Subclasses may use this to monitor whether preloading data for a node should be canceled.
* @note This method is guaranteed to be called on main.
*/
- (void)didExitPreloadState;

/**
* @abstract Called when the node has completed applying the layout.
* @discussion Can be used for operations that are performed after layout has completed.
* @note This method is guaranteed to be called on main.
*/
- (void)nodeDidLayout;

/**
* @abstract Called when the node loads.
* @discussion Can be used for operations that are performed after the node's view is available.
* @note This method is guaranteed to be called on main.
*/
- (void)nodeDidLoad;

/**
* @abstract Indicates that the receiver and all subnodes have finished displaying.
* @discussion May be called more than once, for example if the receiver has a network image node.
* This is called after the first display pass even if network image nodes have not downloaded anything
* (text would be done, and other nodes that are ready to do their final display). Each render of
* every progressive jpeg network node would cause this to be called, so this hook could be called up to
* 1 + (pJPEGcount * pJPEGrenderCount) times. The render count depends on how many times the downloader calls
* the progressImage block.
* @note This method is guaranteed to be called on main.
*/
- (void)hierarchyDisplayDidFinish;

@end
68 changes: 0 additions & 68 deletions Source/ASDisplayNode+Subclasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,74 +42,6 @@ NS_ASSUME_NONNULL_BEGIN
* variables.
*/

@protocol ASInterfaceStateDelegate <NSObject>
@required

/**
* @abstract Called whenever any bit in the ASInterfaceState bitfield is changed.
* @discussion Subclasses may use this to monitor when they become visible, should free cached data, and much more.
* @see ASInterfaceState
*/
- (void)interfaceStateDidChange:(ASInterfaceState)newState fromState:(ASInterfaceState)oldState;

/**
* @abstract Called whenever the node becomes visible.
* @discussion Subclasses may use this to monitor when they become visible.
* @note This method is guaranteed to be called on main.
*/
- (void)didEnterVisibleState;

/**
* @abstract Called whenever the node is no longer visible.
* @discussion Subclasses may use this to monitor when they are no longer visible.
* @note This method is guaranteed to be called on main.
*/
- (void)didExitVisibleState;

/**
* @abstract Called whenever the the node has entered the display state.
* @discussion Subclasses may use this to monitor when a node should be rendering its content.
* @note This method is guaranteed to be called on main.
*/
- (void)didEnterDisplayState;

/**
* @abstract Called whenever the the node has exited the display state.
* @discussion Subclasses may use this to monitor when a node should no longer be rendering its content.
* @note This method is guaranteed to be called on main.
*/
- (void)didExitDisplayState;

/**
* @abstract Called whenever the the node has entered the preload state.
* @discussion Subclasses may use this to monitor data for a node should be preloaded, either from a local or remote source.
* @note This method is guaranteed to be called on main.
*/
- (void)didEnterPreloadState;

/**
* @abstract Called whenever the the node has exited the preload state.
* @discussion Subclasses may use this to monitor whether preloading data for a node should be canceled.
* @note This method is guaranteed to be called on main.
*/
- (void)didExitPreloadState;

/**
* @abstract Called when the node has completed applying the layout.
* @discussion Can be used for operations that are performed after layout has completed.
* @note This method is guaranteed to be called on main.
*/
- (void)nodeDidLayout;

/**
* @abstract Called when the node loads.
* @discussion Can be used for operations that are performed after the node's view is available.
* @note This method is guaranteed to be called on main.
*/
- (void)nodeDidLoad;

@end

@interface ASDisplayNode (Subclassing) <ASInterfaceStateDelegate>

#pragma mark - Properties
Expand Down
50 changes: 19 additions & 31 deletions Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#import <AsyncDisplayKit/_ASAsyncTransactionContainer.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASDisplayNode+InterfaceState.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
Expand Down Expand Up @@ -70,37 +71,6 @@ typedef ASLayoutSpec * _Nonnull(^ASLayoutSpecBlock)(__kindof ASDisplayNode *node
*/
typedef void (^ASDisplayNodeNonFatalErrorBlock)(NSError *error);

/**
* Interface state is available on ASDisplayNode and ASViewController, and
* allows checking whether a node is in an interface situation where it is prudent to trigger certain
* actions: measurement, data loading, display, and visibility (the latter for animations or other onscreen-only effects).
*
* The defualt state, ASInterfaceStateNone, means that the element is not predicted to be onscreen soon and
* preloading should not be performed. Swift: use [] for the default behavior.
*/
typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
{
/** The element is not predicted to be onscreen soon and preloading should not be performed */
ASInterfaceStateNone = 0,
/** The element may be added to a view soon that could become visible. Measure the layout, including size calculation. */
ASInterfaceStateMeasureLayout = 1 << 0,
/** The element is likely enough to come onscreen that disk and/or network data required for display should be fetched. */
ASInterfaceStatePreload = 1 << 1,
/** The element is very likely to become visible, and concurrent rendering should be executed for any -setNeedsDisplay. */
ASInterfaceStateDisplay = 1 << 2,
/** The element is physically onscreen by at least 1 pixel.
In practice, all other bit fields should also be set when this flag is set. */
ASInterfaceStateVisible = 1 << 3,

/**
* The node is not contained in a cell but it is in a window.
*
* Currently we only set `interfaceState` to other values for
* nodes contained in table views or collection views.
*/
ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStatePreload | ASInterfaceStateDisplay | ASInterfaceStateVisible,
};

typedef NS_ENUM(NSInteger, ASCornerRoundingType) {
ASCornerRoundingTypeDefaultSlowCALayer,
ASCornerRoundingTypePrecomposited,
Expand Down Expand Up @@ -292,6 +262,24 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority;
*/
@property (readonly) ASInterfaceState interfaceState;

/**
* @abstract Adds a delegate to receive notifications on interfaceState changes.
*
* @warning This must be called from the main thread.
*
* @see ASInterfaceState
*/
- (void)addInterfaceStateDelegate:(id <ASInterfaceStateDelegate>)interfaceStateDelegate;

/**
* @abstract Removes a delegate from receiving notifications on interfaceState changes.
*
* @warning This must be called from the main thread.
*
* @see ASInterfaceState
*/
- (void)removeInterfaceStateDelegate:(id <ASInterfaceStateDelegate>)interfaceStateDelegate;

/**
* @abstract Class property that allows to set a block that can be called on non-fatal errors. This
* property can be useful for cases when Async Display Kit can recover from an abnormal behavior, but
Expand Down
Loading

0 comments on commit a4f78ad

Please sign in to comment.