Swiftgram/submodules/AsyncDisplayKit/Source/ASDisplayNodeInternal.h
2020-02-27 00:02:03 +04:00

408 lines
15 KiB
Objective-C

//
// ASDisplayNodeInternal.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
// Changes after 4/13/2017 are: Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
//
// The following methods are ONLY for use by _ASDisplayLayer, _ASDisplayView, and ASDisplayNode.
// These methods must never be called or overridden by other classes.
//
#import <atomic>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#import "ASLayoutTransition.h"
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/_ASTransitionContext.h>
#import <AsyncDisplayKit/ASWeakSet.h>
NS_ASSUME_NONNULL_BEGIN
@protocol _ASDisplayLayerDelegate;
@class _ASDisplayLayer;
@class _ASPendingState;
@class ASNodeController;
struct ASDisplayNodeFlags;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector);
BOOL ASDisplayNodeNeedsSpecialPropertiesHandling(BOOL isSynchronous, BOOL isLayerBacked);
/// Get the pending view state for the node, creating one if needed.
_ASPendingState * ASDisplayNodeGetPendingState(ASDisplayNode * node);
typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
{
ASDisplayNodeMethodOverrideNone = 0,
ASDisplayNodeMethodOverrideTouchesBegan = 1 << 0,
ASDisplayNodeMethodOverrideTouchesCancelled = 1 << 1,
ASDisplayNodeMethodOverrideTouchesEnded = 1 << 2,
ASDisplayNodeMethodOverrideTouchesMoved = 1 << 3,
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4,
ASDisplayNodeMethodOverrideCalcLayoutThatFits = 1 << 5,
ASDisplayNodeMethodOverrideCalcSizeThatFits = 1 << 6,
ASDisplayNodeMethodOverrideCanBecomeFirstResponder= 1 << 7,
ASDisplayNodeMethodOverrideBecomeFirstResponder = 1 << 8,
ASDisplayNodeMethodOverrideCanResignFirstResponder= 1 << 9,
ASDisplayNodeMethodOverrideResignFirstResponder = 1 << 10,
ASDisplayNodeMethodOverrideIsFirstResponder = 1 << 11,
};
typedef NS_OPTIONS(uint_least32_t, ASDisplayNodeAtomicFlags)
{
Synchronous = 1 << 0,
YogaLayoutInProgress = 1 << 1,
};
// Can be called without the node's lock. Client is responsible for thread safety.
#define _loaded(node) (node->_layer != nil)
#define checkFlag(flag) ((_atomicFlags.load() & flag) != 0)
// Returns the old value of the flag as a BOOL.
#define setFlag(flag, x) (((x ? _atomicFlags.fetch_or(flag) \
: _atomicFlags.fetch_and(~flag)) & flag) != 0)
AS_EXTERN NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp;
// Allow 2^n increments of begin disabling hierarchy notifications
#define VISIBILITY_NOTIFICATIONS_DISABLED_BITS 4
#define TIME_DISPLAYNODE_OPS 0 // If you're using this information frequently, try: (DEBUG || PROFILE)
#define NUM_CLIP_CORNER_LAYERS 4
@interface ASDisplayNode () <_ASTransitionContextCompletionDelegate>
{
@package
AS::RecursiveMutex __instanceLock__;
_ASPendingState *_pendingViewState;
ASInterfaceState _pendingInterfaceState;
ASInterfaceState _preExitingInterfaceState;
UIView *_view;
CALayer *_layer;
std::atomic<ASDisplayNodeAtomicFlags> _atomicFlags;
struct ASDisplayNodeFlags {
// public properties
unsigned viewEverHadAGestureRecognizerAttached:1;
unsigned layerBacked:1;
unsigned displaysAsynchronously:1;
unsigned rasterizesSubtree:1;
unsigned shouldBypassEnsureDisplay:1;
unsigned displaySuspended:1;
unsigned shouldAnimateSizeChanges:1;
// Wrapped view handling
// The layer contents should not be cleared in case the node is wrapping a UIImageView.UIImageView is specifically
// optimized for performance and does not use the usual way to provide the contents of the CALayer via the
// CALayerDelegate method that backs the UIImageView.
unsigned canClearContentsOfLayer:1;
// Prevent calling setNeedsDisplay on a layer that backs a UIImageView. Usually calling setNeedsDisplay on a CALayer
// triggers a recreation of the contents of layer unfortunately calling it on a CALayer that backs a UIImageView
// it goes through the normal flow to assign the contents to a layer via the CALayerDelegate methods. Unfortunately
// UIImageView does not do recreate the layer contents the usual way, it actually does not implement some of the
// methods at all instead it throws away the contents of the layer and nothing will show up.
unsigned canCallSetNeedsDisplayOfLayer:1;
unsigned implementsDrawRect:1;
unsigned implementsImageDisplay:1;
unsigned implementsDrawParameters:1;
// internal state
unsigned isEnteringHierarchy:1;
unsigned isExitingHierarchy:1;
unsigned isInHierarchy:1;
unsigned visibilityNotificationsDisabled:VISIBILITY_NOTIFICATIONS_DISABLED_BITS;
unsigned isDeallocating:1;
} _flags;
@protected
ASDisplayNode * __weak _supernode;
NSMutableArray<ASDisplayNode *> *_subnodes;
ASNodeController *_strongNodeController;
__weak ASNodeController *_weakNodeController;
// Set this to nil whenever you modify _subnodes
NSArray<ASDisplayNode *> *_cachedSubnodes;
std::atomic_uint _displaySentinel;
// This is the desired contentsScale, not the scale at which the layer's contents should be displayed
CGFloat _contentsScaleForDisplay;
ASDisplayNodeMethodOverrides _methodOverrides;
UIEdgeInsets _hitTestSlop;
#if ASEVENTLOG_ENABLE
ASEventLog *_eventLog;
#endif
// Layout support
ASLayoutElementStyle *_style;
std::atomic<ASPrimitiveTraitCollection> _primitiveTraitCollection;
// Layout Spec
ASLayoutSpecBlock _layoutSpecBlock;
NSString *_debugName;
#if YOGA
// Only ASDisplayNodes are supported in _yogaChildren currently. This means that it is necessary to
// create ASDisplayNodes to make a stack layout when using Yoga.
// However, the implementation is mostly ready for id <ASLayoutElement>, with a few areas requiring updates.
NSMutableArray<ASDisplayNode *> *_yogaChildren;
__weak ASDisplayNode *_yogaParent;
ASLayout *_yogaCalculatedLayout;
#endif
// Automatically manages subnodes
BOOL _automaticallyManagesSubnodes; // Main thread only
// Layout Transition
_ASTransitionContext *_pendingLayoutTransitionContext;
NSTimeInterval _defaultLayoutTransitionDuration;
NSTimeInterval _defaultLayoutTransitionDelay;
UIViewAnimationOptions _defaultLayoutTransitionOptions;
std::atomic<int32_t> _transitionID;
std::atomic<int32_t> _pendingTransitionID;
ASLayoutTransition *_pendingLayoutTransition;
ASDisplayNodeLayout _calculatedDisplayNodeLayout;
ASDisplayNodeLayout _pendingDisplayNodeLayout;
/// Sentinel for layout data. Incremented when we get -setNeedsLayout / -invalidateCalculatedLayout.
/// Starts at 1.
std::atomic<NSUInteger> _layoutVersion;
// Layout Spec performance measurement
ASDisplayNodePerformanceMeasurementOptions _measurementOptions;
NSTimeInterval _layoutSpecTotalTime;
NSInteger _layoutSpecNumberOfPasses;
NSTimeInterval _layoutComputationTotalTime;
NSInteger _layoutComputationNumberOfPasses;
// View Loading
ASDisplayNodeViewBlock _viewBlock;
ASDisplayNodeLayerBlock _layerBlock;
NSMutableArray<ASDisplayNodeDidLoadBlock> *_onDidLoadBlocks;
Class _viewClass; // nil -> _ASDisplayView
Class _layerClass; // nil -> _ASDisplayLayer
// Placeholder support
UIImage *_placeholderImage;
BOOL _placeholderEnabled;
CALayer *_placeholderLayer;
// keeps track of nodes/subnodes that have not finished display, used with placeholders
ASWeakSet *_pendingDisplayNodes;
// Corner Radius support
CGFloat _cornerRadius;
ASCornerRoundingType _cornerRoundingType;
CALayer *_clipCornerLayers[NUM_CLIP_CORNER_LAYERS];
ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext;
ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext;
// Accessibility support
BOOL _isAccessibilityElement;
NSString *_accessibilityLabel;
NSAttributedString *_accessibilityAttributedLabel;
NSString *_accessibilityHint;
NSAttributedString *_accessibilityAttributedHint;
NSString *_accessibilityValue;
NSAttributedString *_accessibilityAttributedValue;
UIAccessibilityTraits _accessibilityTraits;
CGRect _accessibilityFrame;
NSString *_accessibilityLanguage;
BOOL _accessibilityElementsHidden;
BOOL _accessibilityViewIsModal;
BOOL _shouldGroupAccessibilityChildren;
NSString *_accessibilityIdentifier;
UIAccessibilityNavigationStyle _accessibilityNavigationStyle;
NSArray *_accessibilityHeaderElements;
CGPoint _accessibilityActivationPoint;
UIBezierPath *_accessibilityPath;
BOOL _isAccessibilityContainer;
// Safe Area support
// These properties are used on iOS 10 and lower, where safe area is not supported by UIKit.
UIEdgeInsets _fallbackSafeAreaInsets;
BOOL _fallbackInsetsLayoutMarginsFromSafeArea;
BOOL _automaticallyRelayoutOnSafeAreaChanges;
BOOL _automaticallyRelayoutOnLayoutMarginsChanges;
BOOL _isViewControllerRoot;
#pragma mark - ASDisplayNode (Debugging)
ASLayout *_unflattenedLayout;
#if TIME_DISPLAYNODE_OPS
@public
NSTimeInterval _debugTimeToCreateView;
NSTimeInterval _debugTimeToApplyPendingState;
NSTimeInterval _debugTimeToAddSubnodeViews;
NSTimeInterval _debugTimeForDidLoad;
#endif
/// Fast path: tells whether we've ever had an interface state delegate before.
BOOL _hasHadInterfaceStateDelegates;
__weak id<ASInterfaceStateDelegate> _interfaceStateDelegates[AS_MAX_INTERFACE_STATE_DELEGATES];
}
+ (void)scheduleNodeForRecursiveDisplay:(ASDisplayNode *)node;
/// The _ASDisplayLayer backing the node, if any.
@property (nullable, nonatomic, readonly) _ASDisplayLayer *asyncLayer;
/// Bitmask to check which methods an object overrides.
- (ASDisplayNodeMethodOverrides)methodOverrides;
/**
* Invoked before a call to setNeedsLayout to the underlying view
*/
- (void)__setNeedsLayout;
/**
* Invoked after a call to setNeedsDisplay to the underlying view
*/
- (void)__setNeedsDisplay;
/**
* Called whenever the node needs to layout its subnodes and, if it's already loaded, its subviews. Executes the layout pass for the node
*
* This method is thread-safe but asserts thread affinity.
*/
- (void)__layout;
/**
* Internal method to add / replace / insert subnode and remove from supernode without checking if
* node has automaticallyManagesSubnodes set to YES.
*/
- (void)_addSubnode:(ASDisplayNode *)subnode;
- (void)_replaceSubnode:(ASDisplayNode *)oldSubnode withSubnode:(ASDisplayNode *)replacementSubnode;
- (void)_insertSubnode:(ASDisplayNode *)subnode belowSubnode:(ASDisplayNode *)below;
- (void)_insertSubnode:(ASDisplayNode *)subnode aboveSubnode:(ASDisplayNode *)above;
- (void)_insertSubnode:(ASDisplayNode *)subnode atIndex:(NSInteger)idx;
- (void)_removeFromSupernodeIfEqualTo:(ASDisplayNode *)supernode;
- (void)_removeFromSupernode;
// Private API for helper functions / unit tests. Use ASDisplayNodeDisableHierarchyNotifications() to control this.
- (BOOL)__visibilityNotificationsDisabled;
- (BOOL)__selfOrParentHasVisibilityNotificationsDisabled;
- (void)__incrementVisibilityNotificationsDisabled;
- (void)__decrementVisibilityNotificationsDisabled;
// Helper methods for UIResponder forwarding
- (BOOL)__canBecomeFirstResponder;
- (BOOL)__becomeFirstResponder;
- (BOOL)__canResignFirstResponder;
- (BOOL)__resignFirstResponder;
- (BOOL)__isFirstResponder;
/// Helper method to summarize whether or not the node run through the display process
- (BOOL)_implementsDisplay;
/// Display the node's view/layer immediately on the current thread, bypassing the background thread rendering. Will be deprecated.
- (void)displayImmediately;
/// Refreshes any precomposited or drawn clip corners, setting up state as required to transition radius or rounding type.
- (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType cornerRadius:(CGFloat)newCornerRadius;
/// Alternative initialiser for backing with a custom view class. Supports asynchronous display with _ASDisplayView subclasses.
- (instancetype)initWithViewClass:(Class)viewClass;
/// Alternative initialiser for backing with a custom layer class. Supports asynchronous display with _ASDisplayLayer subclasses.
- (instancetype)initWithLayerClass:(Class)layerClass;
@property (nonatomic) CGFloat contentsScaleForDisplay;
- (void)applyPendingViewState;
/**
* Makes a local copy of the interface state delegates then calls the block on each.
*
* Lock is not held during block invocation. Method must not be called with the lock held.
*/
- (void)enumerateInterfaceStateDelegates:(void(NS_NOESCAPE ^)(id<ASInterfaceStateDelegate> delegate))block;
/**
* // TODO: NOT YET IMPLEMENTED
*
* @abstract Prevents interface state changes from affecting the node, until disabled.
*
* @discussion Useful to avoid flashing after removing a node from the hierarchy and re-adding it.
* Removing a node from the hierarchy will cause it to exit the Display state, clearing its contents.
* For some animations, it's desirable to be able to remove a node without causing it to re-display.
* Once re-enabled, the interface state will be updated to the same value it would have been.
*
* @see ASInterfaceState
*/
@property (nonatomic) BOOL interfaceStateSuspended;
/**
* This method has proven helpful in a few rare scenarios, similar to a category extension on UIView,
* but it's considered private API for now and its use should not be encouraged.
* @param checkViewHierarchy If YES, and no supernode can be found, method will walk up from `self.view` to find a supernode.
* If YES, this method must be called on the main thread and the node must not be layer-backed.
*/
- (nullable ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass checkViewHierarchy:(BOOL)checkViewHierarchy;
/**
* Whether this node rasterizes its descendants. See -enableSubtreeRasterization.
*/
@property (readonly) BOOL rasterizesSubtree;
/**
* Called if a gesture recognizer was attached to an _ASDisplayView
*/
- (void)nodeViewDidAddGestureRecognizer;
// Recalculates fallbackSafeAreaInsets for the subnodes
- (void)_fallbackUpdateSafeAreaOnChildren;
@end
@interface ASDisplayNode (InternalPropertyBridge)
@property (nonatomic) CGFloat layerCornerRadius;
- (BOOL)_locked_insetsLayoutMarginsFromSafeArea;
@end
@interface ASDisplayNode (ASLayoutElementPrivate)
/**
* Returns the internal style object or creates a new if no exists. Need to be called with lock held.
*/
- (ASLayoutElementStyle *)_locked_style;
/**
* Returns the current layout element. Need to be called with lock held.
*/
- (id<ASLayoutElement>)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize;
@end
NS_ASSUME_NONNULL_END