/* Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import #import #import #import #import /** * The subclass header _ASDisplayNode+Subclasses_ defines the following methods that either must or can be overriden by * subclasses of ASDisplayNode. * * These methods should never be called directly by other classes. * * ## Drawing * * Implement one of +displayAsyncLayer:parameters:isCancelled: or +drawRect:withParameters:isCancelled: to provide * drawing for your node. * * Use -drawParametersForAsyncLayer: to copy any properties that are involved in drawing into an immutable object for * use on the display queue. The display and drawRect implementations *MUST* be thread-safe, as they can be called on * the displayQueue (asynchronously) or the main thread (synchronously/displayImmediately). * * Class methods that require passing in copies of the values are used to minimize the need for locking around instance * variable access, and the possibility of the asynchronous display pass grabbing an inconsistent state across multiple * variables. */ @interface ASDisplayNode (ASDisplayNodeSubclasses) /** @name View Configuration */ /** * @return The view class to use when creating a new display node instance. Defaults to _ASDisplayView. */ + (Class)viewClass; /** @name Properties */ /** * @abstract The scale factor to apply to the rendering. * * @discussion Use setNeedsDisplayAtScale: to set a value and then after display, the display node will set the layer's * contentsScale. This is to prevent jumps when re-rasterizing at a different contentsScale. * Read this property if you need to know the future contentsScale of your layer, eg in drawParameters. * * @see setNeedsDisplayAtScale: */ @property (nonatomic, assign, readonly) CGFloat contentsScaleForDisplay; /** * @abstract Whether the view or layer of this display node is currently in a window */ @property (nonatomic, readonly, assign, getter=isInWindow) BOOL inWindow; /** @name View Lifecycle */ /** * @abstract Called on the main thread immediately after self.view is created. * * @discussion This is the best time to add gesture recognizers to the view. */ - (void)didLoad ASDISPLAYNODE_REQUIRES_SUPER; /** @name Layout */ /** * @abstract Called on the main thread by the view's -layoutSubviews. * * @discussion Subclasses override this method to layout all subnodes or subviews. */ - (void)layout; /** * @abstract Called on the main thread by the view's -layoutSubviews, after -layout. * * @discussion Gives a chance for subclasses to perform actions after the subclass and superclass have finished laying * out. */ - (void)layoutDidFinish; /** @name Sizing */ /** * @abstract Return the calculated size. * * @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned size * is cached by ASDisplayNode for quick access during -layout, via -calculatedSize. Other expensive work that needs to * be done before display can be performed here, and using ivars to cache any valuable intermediate results is * encouraged. * * @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedSize instead. */ - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize; /** * @abstract Invalidate previously measured and cached size. * * @discussion Subclasses should call this method to invalidate the previously measured and cached size for the display * node, when the contents of the node change in such a way as to require measuring it again. */ - (void)invalidateCalculatedSize; /** @name Drawing */ /** * @summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set * to an appropriate context. * * @param parameters An object describing all of the properties you need to draw. Return this from * -drawParametersForAsyncLayer: * @param isCancelled Execute this block to check whether the current drawing operation has been cancelled to avoid * unnecessary work. A return value of YES means cancel drawing and return. * @param isRasterizing YES if the layer is being rasterized into another layer, in which case drawRect: probably wants * to avoid doing things like filling its bounds with a zero-alpha color to clear the backing store. * * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) */ + (void)drawRect:(CGRect)bounds withParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock isRasterizing:(BOOL)isRasterizing; /** * @summary Delegate override to provide new layer contents as a UIImage. * * @param parameters An object describing all of the properties you need to draw. Return this from * -drawParametersForAsyncLayer: * @param isCancelled Execute this block to check whether the current drawing operation has been cancelled to avoid * unnecessary work. A return value of YES means cancel drawing and return. * * @return A UIImage with contents that are ready to display on the main thread. Make sure that the image is already * decoded before returning it here. * * @note Called on the display queue and/or main queue (MUST BE THREAD SAFE) */ + (UIImage *)displayWithParameters:(id)parameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelledBlock; /** * @abstract Delegate override for drawParameters * * @note Called on the main thread only */ - (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer; /** * @abstract Indicates that the receiver has finished displaying. * * @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) has * completed. */ - (void)displayDidFinish; /** * @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value. * * @discussion Subclasses should override this if they don't want their contentsScale changed. * * @note This changes an internal property. * -setNeedsDisplay is also available to trigger display without changing contentsScaleForDisplay. * @see contentsScaleForDisplay */ - (void)setNeedsDisplayAtScale:(CGFloat)contentsScale; /** * @abstract Recursively calls setNeedsDisplayAtScale: on subnodes. * * @discussion Subclasses may override this if they require modifying the scale set on their child nodes. * * @note Only the node tree is walked, not the view or layer trees. * * @see setNeedsDisplayAtScale: * @see contentsScaleForDisplay */ - (void)recursivelySetNeedsDisplayAtScale:(CGFloat)contentsScale; /** @name Touch handling */ /** * @abstract Tells the node when touches began in its view. * * @param touches A set of UITouch instances. * @param event A UIEvent associated with the touch. */ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; /** * @abstract Tells the node when touches moved in its view. * * @param touches A set of UITouch instances. * @param event A UIEvent associated with the touch. */ - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; /** * @abstract Tells the node when touches ended in its view. * * @param touches A set of UITouch instances. * @param event A UIEvent associated with the touch. */ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; /** * @abstract Tells the node when touches was cancelled in its view. * * @param touches A set of UITouch instances. * @param event A UIEvent associated with the touch. */ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; /** @name Managing Gesture Recognizers */ /** * @abstract Asks the node if a gesture recognizer should continue tracking touches. * * @param gestureRecognizer A gesture recognizer trying to recognize a gesture. */ - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer; /** @name Hit Testing */ /** * @abstract Returns the view that contains the point. * * @discussion Override to make this node respond differently to touches: (e.g. hide touches from subviews, send all * touches to certain subviews (hit area maximizing), etc.) * * @param point A point specified in the node's local coordinate system (bounds). * @param event The event that warranted a call to this method. * * @return Returns a UIView, not ASDisplayNode, for two reasons: * 1) allows sending events to plain UIViews that don't have attached nodes, * 2) hitTest: is never called before the views are created. */ - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; /** @name Observing node-related changes */ // Called just before the view is added to a superview. - (void)willEnterHierarchy; // Called after the view is removed from the window. - (void)didExitHierarchy; // Called by -recursivelyReclaimMemory. Provides an opportunity to clear backing store and other memory-intensive intermediates, // such as text layout managers or downloaded content that can be written to a disk cache. // Base class implements self.contents = nil, clearing any backing store, for asynchronous regeneration when needed. - (void)reclaimMemory; /** @name Description */ /** * @abstract Return a description of the node * * @discussion The function that gets called for each display node in -recursiveDescription */ - (NSString *)descriptionForRecursiveDescription; @end @interface ASDisplayNode (ASDisplayNodePrivate) // 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. - (ASDisplayNode *)_supernodeWithClass:(Class)supernodeClass; @end #define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity") #define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity")