Merge commit '7621e2f8dec938cf48181c8b10afc9b01f444e68' into beta

This commit is contained in:
Ilya Laktyushin
2025-12-06 02:17:48 +04:00
commit 8344b97e03
28070 changed files with 7995182 additions and 0 deletions
@@ -0,0 +1,27 @@
//
// ASAbsoluteLayoutElement.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
//
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Layout options that can be defined for an ASLayoutElement being added to a ASAbsoluteLayoutSpec.
*/
@protocol ASAbsoluteLayoutElement
/**
* @abstract The position of this object within its parent spec.
*/
@property (nonatomic) CGPoint layoutPosition;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,61 @@
//
// ASAsciiArtBoxCreator.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASLayoutElementAsciiArtProtocol <NSObject>
/**
* Returns an ascii-art representation of this object and its children.
* For example, an ASInsetSpec may return something like this:
*
* --ASInsetLayoutSpec--
* | ASTextNode |
* ---------------------
*/
- (NSString *)asciiArtString;
/**
* returns the name of this object that will display in the ascii art. Usually this can
* simply be NSStringFromClass([self class]).
*/
- (NSString *)asciiArtName;
@end
/**
* A that takes a parent and its children and renders as ascii art box.
*/
@interface ASAsciiArtBoxCreator : NSObject
/**
* Renders an ascii art box with the children aligned horizontally
* Example:
* ------------ASStackLayoutSpec-----------
* | ASTextNode ASTextNode ASTextNode |
* ----------------------------------------
*/
+ (NSString *)horizontalBoxStringForChildren:(NSArray<NSString *> *)children parent:(NSString *)parent;
/**
* Renders an ascii art box with the children aligned vertically.
* Example:
* --ASStackLayoutSpec--
* | ASTextNode |
* | ASTextNode |
* | ASTextNode |
* ---------------------
*/
+ (NSString *)verticalBoxStringForChildren:(NSArray<NSString *> *)children parent:(NSString *)parent;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,102 @@
//
// ASAssert.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
//
#pragma once
#import <UIKit/UIKit.h>
#import <Foundation/NSException.h>
#import <pthread.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#if !defined(NS_BLOCK_ASSERTIONS)
#define ASDISPLAYNODE_ASSERTIONS_ENABLED 1
#else
#define ASDISPLAYNODE_ASSERTIONS_ENABLED 0
#endif
/**
* Note: In some cases it would be sufficient to do e.g.:
* ASDisplayNodeAssert(...) NSAssert(__VA_ARGS__)
* but we prefer not to, because we want to match the autocomplete behavior of NSAssert.
* The construction listed above does not show the user what arguments are required and what are optional.
*/
#define ASDisplayNodeAssert(condition, desc, ...) NSAssert(condition, desc, ##__VA_ARGS__)
#define ASDisplayNodeCAssert(condition, desc, ...) NSCAssert(condition, desc, ##__VA_ARGS__)
#define ASDisplayNodeAssertNil(condition, desc, ...) ASDisplayNodeAssert((condition) == nil, desc, ##__VA_ARGS__)
#define ASDisplayNodeCAssertNil(condition, desc, ...) ASDisplayNodeCAssert((condition) == nil, desc, ##__VA_ARGS__)
#define ASDisplayNodeAssertNotNil(condition, desc, ...) ASDisplayNodeAssert((condition) != nil, desc, ##__VA_ARGS__)
#define ASDisplayNodeCAssertNotNil(condition, desc, ...) ASDisplayNodeCAssert((condition) != nil, desc, ##__VA_ARGS__)
#define ASDisplayNodeAssertImplementedBySubclass() ASDisplayNodeAssert(NO, @"This method must be implemented by subclass %@", [self class]);
#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssert(NO, nil, @"This class is not instantiable.");
#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssert(NO, nil, @"This method is not supported by class %@", [self class]);
#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This method must be called on the main thread")
#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This function must be called on the main thread")
#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssert(0 == pthread_main_np(), @"This method must be called off the main thread")
#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssert(0 == pthread_main_np(), @"This function must be called off the main thread")
#define ASDisplayNodeAssertFlag(X, desc, ...) ASDisplayNodeAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__)
#define ASDisplayNodeCAssertFlag(X, desc, ...) ASDisplayNodeCAssert((1 == __builtin_popcount(X)), desc, ##__VA_ARGS__)
#define ASDisplayNodeAssertTrue(condition) ASDisplayNodeAssert((condition), @"Expected %s to be true.", #condition)
#define ASDisplayNodeCAssertTrue(condition) ASDisplayNodeCAssert((condition), @"Expected %s to be true.", #condition)
#define ASDisplayNodeAssertFalse(condition) ASDisplayNodeAssert(!(condition), @"Expected %s to be false.", #condition)
#define ASDisplayNodeCAssertFalse(condition) ASDisplayNodeCAssert(!(condition), @"Expected %s to be false.", #condition)
#define ASDisplayNodeFailAssert(desc, ...) ASDisplayNodeAssert(NO, desc, ##__VA_ARGS__)
#define ASDisplayNodeCFailAssert(desc, ...) ASDisplayNodeCAssert(NO, desc, ##__VA_ARGS__)
#define ASDisplayNodeConditionalAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__)
#define ASDisplayNodeConditionalCAssert(shouldTestCondition, condition, desc, ...) ASDisplayNodeCAssert((!(shouldTestCondition) || (condition)), desc, ##__VA_ARGS__)
#define ASDisplayNodeCAssertPositiveReal(description, num) ASDisplayNodeCAssert(num >= 0 && num <= CGFLOAT_MAX, @"%@ must be a real positive integer: %f.", description, (CGFloat)num)
#define ASDisplayNodeCAssertInfOrPositiveReal(description, num) ASDisplayNodeCAssert(isinf(num) || (num >= 0 && num <= CGFLOAT_MAX), @"%@ must be infinite or a real positive integer: %f.", description, (CGFloat)num)
#define ASDisplayNodeCAssertPermanent(object) ASDisplayNodeCAssert(CFGetRetainCount((__bridge CFTypeRef)(object)) == CFGetRetainCount(kCFNull), @"Expected %s to be a permanent object.", #object)
#define ASDisplayNodeErrorDomain @"ASDisplayNodeErrorDomain"
#define ASDisplayNodeNonFatalErrorCode 1
/**
* In debug methods, it can be useful to disable main thread assertions to get valuable information,
* even if it means violating threading requirements. These functions are used in -debugDescription and let
* threads decide to suppress/re-enable main thread assertions.
*/
#pragma mark - Main Thread Assertions Disabling
AS_EXTERN BOOL ASMainThreadAssertionsAreDisabled(void);
AS_EXTERN void ASPushMainThreadAssertionsDisabled(void);
AS_EXTERN void ASPopMainThreadAssertionsDisabled(void);
#pragma mark - Non-Fatal Assertions
/// Returns YES if assertion passed, NO otherwise.
#define ASDisplayNodeAssertNonFatal(condition, desc, ...) ({ \
BOOL __evaluated = condition; \
if (__evaluated == NO) { \
ASDisplayNodeFailAssert(desc, ##__VA_ARGS__); \
ASDisplayNodeNonFatalErrorBlock block = [ASDisplayNode nonFatalErrorBlock]; \
if (block != nil) { \
NSDictionary *userInfo = nil; \
if (desc.length > 0) { \
userInfo = @{ NSLocalizedDescriptionKey : desc }; \
} \
NSError *error = [NSError errorWithDomain:ASDisplayNodeErrorDomain code:ASDisplayNodeNonFatalErrorCode userInfo:userInfo]; \
block(error); \
} \
} \
__evaluated; \
}) \
@@ -0,0 +1,85 @@
//
// ASAvailability.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
//
#import <CoreFoundation/CFBase.h>
#pragma once
#define AS_TLS_AVAILABLE 1
#ifndef AS_ENABLE_TEXTNODE
#define AS_ENABLE_TEXTNODE 1 // Enable old TextNode by default
#endif
// This needs to stay in sync with Weaver
#ifndef AS_USE_VIDEO
#define AS_USE_VIDEO 0
#endif
#ifndef AS_USE_PHOTOS
#define AS_USE_PHOTOS 0
#endif
#ifndef AS_USE_MAPKIT
#define AS_USE_MAPKIT 0
#endif
#ifndef AS_USE_ASSETS_LIBRARY
#define AS_USE_ASSETS_LIBRARY 0
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_10_0
#define kCFCoreFoundationVersionNumber_iOS_10_0 1348.00
#endif
#ifndef kCFCoreFoundationVersionNumber_iOS_11_0
#define kCFCoreFoundationVersionNumber_iOS_11_0 1438.10
#endif
#ifndef __IPHONE_11_0
#define __IPHONE_11_0 110000
#endif
#define AS_AT_LEAST_IOS10 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_10_0)
#define AS_AT_LEAST_IOS11 (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_11_0)
// Use __builtin_available if we're on Xcode >= 9, AS_AT_LEAST otherwise.
#if __has_builtin(__builtin_available)
#define AS_AVAILABLE_IOS(ver) __builtin_available(iOS ver, *)
#define AS_AVAILABLE_TVOS(ver) __builtin_available(tvOS ver, *)
#define AS_AVAILABLE_IOS_TVOS(ver1, ver2) __builtin_available(iOS ver1, tvOS ver2, *)
#else
#define AS_AVAILABLE_IOS(ver) (TARGET_OS_IOS && AS_AT_LEAST_IOS##ver)
#define AS_AVAILABLE_TVOS(ver) (TARGET_OS_TV && AS_AT_LEAST_IOS##ver)
#define AS_AVAILABLE_IOS_TVOS(ver1, ver2) (AS_AVAILABLE_IOS(ver1) || AS_AVAILABLE_TVOS(ver2))
#endif
// If Yoga is available, make it available anywhere we use ASAvailability.
// This reduces Yoga-specific code in other files.
// NOTE: Yoga integration is experimental and not fully tested. Use with caution and test layouts carefully.
#ifndef YOGA_HEADER_PATH
#define YOGA_HEADER_PATH <yoga/Yoga.h>
#endif
#ifndef YOGA
#define YOGA __has_include(YOGA_HEADER_PATH)
#endif
#ifdef ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE
#error "ASTEXTNODE_EXPERIMENT_GLOBAL_ENABLE is unavailable. See ASConfiguration.h."
#endif
#define AS_PIN_REMOTE_IMAGE __has_include(<PINRemoteImage/PINRemoteImage.h>)
#define AS_IG_LIST_KIT __has_include(<IGListKit/IGListKit.h>)
/**
* For IGListKit versions < 3.0, you have to use IGListCollectionView.
* For 3.0 and later, that class is removed and you use UICollectionView.
*/
#define IG_LIST_COLLECTION_VIEW __has_include(<IGListKit/IGListCollectionView.h>)
@@ -0,0 +1,259 @@
//
// ASBaseDefines.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#define AS_EXTERN FOUNDATION_EXTERN
#define unowned __unsafe_unretained
/**
* Hack to support building for iOS with Xcode 9. UIUserInterfaceStyle was previously tvOS-only,
* and it was added to iOS 12. Xcode 9 (iOS 11 SDK) will flat-out refuse to build anything that
* references this enum targeting iOS, even if it's guarded with the right availability macros,
* because it thinks the entire platform isn't compatible with the enum.
*/
#if TARGET_OS_TV || (defined(__IPHONE_12_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_12_0)
#define AS_BUILD_UIUSERINTERFACESTYLE 1
#else
#define AS_BUILD_UIUSERINTERFACESTYLE 0
#endif
/**
* Decorates methods that clients can implement in categories on our base class. These methods
* will be stubbed with an empty implementation if no implementation is provided.
*/
#define AS_CATEGORY_IMPLEMENTABLE
#ifdef __GNUC__
# define ASDISPLAYNODE_GNUC(major, minor) \
(__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
#else
# define ASDISPLAYNODE_GNUC(major, minor) 0
#endif
#ifndef ASDISPLAYNODE_INLINE
# if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
# define ASDISPLAYNODE_INLINE static inline
# elif defined (__MWERKS__) || defined (__cplusplus)
# define ASDISPLAYNODE_INLINE static inline
# elif ASDISPLAYNODE_GNUC (3, 0)
# define ASDISPLAYNODE_INLINE static __inline__ __attribute__ ((always_inline))
# else
# define ASDISPLAYNODE_INLINE static
# endif
#endif
#ifndef ASDISPLAYNODE_WARN_DEPRECATED
# define ASDISPLAYNODE_WARN_DEPRECATED 1
#endif
#ifndef ASDISPLAYNODE_DEPRECATED
# if ASDISPLAYNODE_GNUC (3, 0) && ASDISPLAYNODE_WARN_DEPRECATED
# define ASDISPLAYNODE_DEPRECATED __attribute__ ((deprecated))
# else
# define ASDISPLAYNODE_DEPRECATED
# endif
#endif
#ifndef ASDISPLAYNODE_DEPRECATED_MSG
# if ASDISPLAYNODE_GNUC (3, 0) && ASDISPLAYNODE_WARN_DEPRECATED
# define ASDISPLAYNODE_DEPRECATED_MSG(msg) __deprecated_msg(msg)
# else
# define ASDISPLAYNODE_DEPRECATED_MSG(msg)
# endif
#endif
#ifndef AS_ENABLE_TIPS
#define AS_ENABLE_TIPS 0
#endif
/**
* The event backtraces take a static 2KB of memory
* and retain all objects present in all the registers
* of the stack frames. The memory consumption impact
* is too significant even to be enabled during general
* development.
*/
#ifndef AS_SAVE_EVENT_BACKTRACES
# define AS_SAVE_EVENT_BACKTRACES 0
#endif
#ifndef __has_feature // Optional.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#ifndef __has_attribute // Optional.
#define __has_attribute(x) 0 // Compatibility with non-clang compilers.
#endif
#ifndef NS_RETURNS_RETAINED
#if __has_feature(attribute_ns_returns_retained)
#define NS_RETURNS_RETAINED __attribute__((ns_returns_retained))
#else
#define NS_RETURNS_RETAINED
#endif
#endif
#ifndef CF_RETURNS_RETAINED
#if __has_feature(attribute_cf_returns_retained)
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
#else
#define CF_RETURNS_RETAINED
#endif
#endif
#ifndef ASDISPLAYNODE_REQUIRES_SUPER
#if __has_attribute(objc_requires_super)
#define ASDISPLAYNODE_REQUIRES_SUPER __attribute__((objc_requires_super))
#else
#define ASDISPLAYNODE_REQUIRES_SUPER
#endif
#endif
#ifndef AS_UNAVAILABLE
#if __has_attribute(unavailable)
#define AS_UNAVAILABLE(message) __attribute__((unavailable(message)))
#else
#define AS_UNAVAILABLE(message)
#endif
#endif
#ifndef AS_WARN_UNUSED_RESULT
#if __has_attribute(warn_unused_result)
#define AS_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
#define AS_WARN_UNUSED_RESULT
#endif
#endif
#define ASOVERLOADABLE __attribute__((overloadable))
#if __has_attribute(noescape)
#define AS_NOESCAPE __attribute__((noescape))
#else
#define AS_NOESCAPE
#endif
#if __has_attribute(objc_subclassing_restricted)
#define AS_SUBCLASSING_RESTRICTED __attribute__((objc_subclassing_restricted))
#else
#define AS_SUBCLASSING_RESTRICTED
#endif
#define AS_ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define ASCreateOnce(expr) ({ \
static dispatch_once_t onceToken; \
static __typeof__(expr) staticVar; \
dispatch_once(&onceToken, ^{ \
staticVar = expr; \
}); \
staticVar; \
})
/// Ensure that class is of certain kind
#define ASDynamicCast(x, c) ({ \
id __val = x;\
((c *) ([__val isKindOfClass:[c class]] ? __val : nil));\
})
/// Ensure that class is of certain kind, assuming it is subclass restricted
#define ASDynamicCastStrict(x, c) ({ \
id __val = x;\
((c *) ([__val class] == [c class] ? __val : nil));\
})
// Compare two primitives, assign if different. Returns whether the assignment happened.
#define ASCompareAssign(lvalue, newValue) ({ \
BOOL result = (lvalue != newValue); \
if (result) { lvalue = newValue; } \
result; \
})
#define ASCompareAssignObjects(lvalue, newValue) \
ASCompareAssignCustom(lvalue, newValue, ASObjectIsEqual)
// e.g. ASCompareAssignCustom(_myInsets, insets, UIEdgeInsetsEqualToEdgeInsets)
#define ASCompareAssignCustom(lvalue, newValue, isequal) ({ \
BOOL result = !(isequal(lvalue, newValue)); \
if (result) { lvalue = newValue; } \
result; \
})
#define ASCompareAssignCopy(lvalue, newValue) ({ \
BOOL result = !ASObjectIsEqual(lvalue, newValue); \
if (result) { lvalue = [newValue copyWithZone:NULL]; } \
result; \
})
/**
* Create a new set by mapping `collection` over `work`, ignoring nil.
*/
#define ASSetByFlatMapping(collection, decl, work) ({ \
NSMutableSet *s = [[NSMutableSet alloc] init]; \
for (decl in collection) {\
id result = work; \
if (result != nil) { \
[s addObject:result]; \
} \
} \
s; \
})
/**
* Create a new ObjectPointerPersonality NSHashTable by mapping `collection` over `work`, ignoring nil.
*
* capacity: 0 is taken from +hashTableWithOptions.
*/
#define ASPointerTableByFlatMapping(collection, decl, work) ({ \
NSHashTable *t = [[NSHashTable alloc] initWithOptions:NSHashTableObjectPointerPersonality capacity:0]; \
for (decl in collection) {\
id result = work; \
if (result != nil) { \
[t addObject:result]; \
} \
} \
t; \
})
/**
* Create a new array by mapping `collection` over `work`, ignoring nil.
*/
#define ASArrayByFlatMapping(collectionArg, decl, work) ({ \
id __collection = collectionArg; \
NSArray *__result; \
if (__collection) { \
id __buf[[__collection count]]; \
NSUInteger __i = 0; \
for (decl in __collection) {\
if ((__buf[__i] = work)) { \
__i++; \
} \
} \
__result = [NSArray arrayByTransferring:__buf count:__i]; \
} \
__result; \
})
/**
* Capture-and-clear a strong reference without the intervening retain/release pair.
*
* E.g. const auto localVar = ASTransferStrong(_myIvar);
* Post-condition: localVar has the strong value from _myIvar and _myIvar is nil.
* No retain/release is emitted when the optimizer is on.
*/
#define ASTransferStrong(lvalue) ({ \
CFTypeRef *__rawPtr = (CFTypeRef *)(void *)(&(lvalue)); \
CFTypeRef __cfValue = *__rawPtr; \
*__rawPtr = NULL; \
__typeof(lvalue) __result = (__bridge_transfer __typeof(lvalue))__cfValue; \
__result; \
})
@@ -0,0 +1,21 @@
//
// ASBlockTypes.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class ASCellNode;
/**
* ASCellNode creation block. Used to lazily create the ASCellNode instance for a specified indexPath.
*/
typedef ASCellNode * _Nonnull(^ASCellNodeBlock)(void);
// Type for the cancellation checker block passed into the async display blocks. YES means the operation has been cancelled, NO means continue.
typedef BOOL(^asdisplaynode_iscancelled_block_t)(void);
@@ -0,0 +1,28 @@
//
// ASCGImageBuffer.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <CoreGraphics/CGDataProvider.h>
NS_ASSUME_NONNULL_BEGIN
AS_SUBCLASSING_RESTRICTED
@interface ASCGImageBuffer : NSObject
/// Init a zero-filled buffer with the given length.
- (instancetype)initWithLength:(NSUInteger)length;
@property (readonly) void *mutableBytes NS_RETURNS_INNER_POINTER;
/// Don't do any drawing or call any methods after calling this.
- (CGDataProviderRef)createDataProviderAndInvalidate CF_RETURNS_RETAINED;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,39 @@
//
// ASCollections.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSArray<__covariant ObjectType> (ASCollections)
/**
* Create an immutable NSArray from a C-array of strong pointers.
*
* Note: The memory for the array you pass in will be zero'd (to prevent ARC from releasing
* the references when the array goes out of scope.)
*
* Can be combined with vector like:
* vector<NSString *> vec;
* vec.push_back(@"foo");
* vec.push_back(@"bar");
* NSArray *arr = [NSArray arrayTransferring:vec.data() count:vec.size()]
* ** vec is now { nil, nil } **
*
* Unfortunately making a convenience method to do this is currently impossible because
* vector<NSString *> can't be converted to vector<id> by the compiler (silly).
*
* See the private __CFArrayCreateTransfer function.
*/
+ (NSArray<ObjectType> *)arrayByTransferring:(ObjectType _Nonnull __strong * _Nonnull)pointers
count:(NSUInteger)count NS_RETURNS_RETAINED;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,59 @@
//
// ASConfiguration.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASExperimentalFeatures.h>
@protocol ASConfigurationDelegate;
NS_ASSUME_NONNULL_BEGIN
static NSInteger const ASConfigurationSchemaCurrentVersion = 1;
AS_SUBCLASSING_RESTRICTED
@interface ASConfiguration : NSObject <NSCopying>
/**
* Initialize this configuration with the provided dictionary,
* or nil to create an empty configuration.
*
* The schema is located in `schemas/configuration.json`.
*/
- (instancetype)initWithDictionary:(nullable NSDictionary *)dictionary;
/**
* The delegate for configuration-related events.
* Delegate methods are called from a serial queue.
*/
@property (nonatomic, nullable) id<ASConfigurationDelegate> delegate;
/**
* The experimental features to enable in Texture.
* See ASExperimentalFeatures for functions to convert to/from a string array.
*/
@property (nonatomic) ASExperimentalFeatures experimentalFeatures;
@end
/**
* Implement this method in a category to make your
* configuration available to Texture. It will be read
* only once and copied.
*
* NOTE: To specify your configuration at compile-time, you can
* define AS_FIXED_CONFIG_JSON as a C-string of JSON. This method
* will then be implemented to parse that string and generate
* a configuration.
*/
@interface ASConfiguration (UserProvided)
+ (ASConfiguration *)textureConfiguration NS_RETURNS_RETAINED;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,38 @@
//
// ASConfigurationDelegate.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASConfiguration.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Used to communicate configuration-related events to the client.
*/
@protocol ASConfigurationDelegate <NSObject>
/**
* Texture performed its first behavior related to the feature(s).
* This can be useful for tracking the impact of the behavior (A/B testing).
*/
- (void)textureDidActivateExperimentalFeatures:(ASExperimentalFeatures)features;
@optional
/**
* Texture framework initialized. This method is called synchronously
* on the main thread from ASInitializeFrameworkMainThread if you defined
* AS_INITIALIZE_FRAMEWORK_MANUALLY or from the default initialization point
* (currently +load) otherwise.
*/
- (void)textureDidInitialize;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,58 @@
//
// ASConfigurationInternal.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
/// Note this has to be public because it's imported by public header ASThread.h =/
/// It will be private again after exp_unfair_lock ends.
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASConfiguration.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Quickly check if an experiment is enabled and notify the delegate
* that it's been activated.
*
* The delegate will be notified asynchronously.
*/
#if DEBUG
#define ASActivateExperimentalFeature(opt) _ASActivateExperimentalFeature(opt)
#else
#define ASActivateExperimentalFeature(opt) ({\
static BOOL result;\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{ result = _ASActivateExperimentalFeature(opt); });\
result;\
})
#endif
/**
* Internal function. Use the macro without the underbar.
*/
AS_EXTERN BOOL _ASActivateExperimentalFeature(ASExperimentalFeatures option);
/**
* Notify the configuration delegate that the framework initialized, if needed.
*/
AS_EXTERN void ASNotifyInitialized(void);
AS_SUBCLASSING_RESTRICTED
@interface ASConfigurationManager : NSObject
/**
* No API for now.
* Just use ASActivateExperimentalFeature to access this efficiently.
*/
/* Exposed for testing purposes only */
+ (void)test_resetWithConfiguration:(nullable ASConfiguration *)configuration;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,73 @@
//
// ASContextTransitioning.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDimension.h>
@class ASDisplayNode;
@class ASLayout;
NS_ASSUME_NONNULL_BEGIN
AS_EXTERN NSString * const ASTransitionContextFromLayoutKey;
AS_EXTERN NSString * const ASTransitionContextToLayoutKey;
@protocol ASContextTransitioning <NSObject>
/**
@abstract Defines if the given transition is animated
*/
- (BOOL)isAnimated;
/**
* @abstract Retrieve either the "from" or "to" layout
*/
- (nullable ASLayout *)layoutForKey:(NSString *)key;
/**
* @abstract Retrieve either the "from" or "to" constrainedSize
*/
- (ASSizeRange)constrainedSizeForKey:(NSString *)key;
/**
* @abstract Retrieve the subnodes from either the "from" or "to" layout
*/
- (NSArray<ASDisplayNode *> *)subnodesForKey:(NSString *)key;
/**
* @abstract Subnodes that have been inserted in the layout transition
*/
- (NSArray<ASDisplayNode *> *)insertedSubnodes;
/**
* @abstract Subnodes that will be removed in the layout transition
*/
- (NSArray<ASDisplayNode *> *)removedSubnodes;
/**
@abstract The frame for the given node before the transition began.
@discussion Returns CGRectNull if the node was not in the hierarchy before the transition.
*/
- (CGRect)initialFrameForNode:(ASDisplayNode *)node;
/**
@abstract The frame for the given node when the transition completes.
@discussion Returns CGRectNull if the node is no longer in the hierarchy after the transition.
*/
- (CGRect)finalFrameForNode:(ASDisplayNode *)node;
/**
@abstract Invoke this method when the transition is completed in `animateLayoutTransition:`
@discussion Passing NO to `didComplete` will set the original layout as the new layout.
*/
- (void)completeTransition:(BOOL)didComplete;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,68 @@
//
// ASControlNode+Subclasses.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
//
#import <AsyncDisplayKit/ASControlNode.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
* The subclass header _ASControlNode+Subclasses_ defines methods to be
* overridden by custom nodes that subclass ASControlNode.
*
* These methods should never be called directly by other classes.
*/
@interface ASControlNode (Subclassing)
/**
@abstract Sends action messages for the given control events.
@param controlEvents A bitmask whose set flags specify the control events for which action messages are sent. See "Control Events" in ASControlNode.h for bitmask constants.
@param touchEvent An event object encapsulating the information specific to the user event.
@discussion ASControlNode implements this method to send all action messages associated with controlEvents. The list of targets is constructed from prior invocations of addTarget:action:forControlEvents:.
*/
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent to the control when tracking begins.
@param touch The touch on the receiving control.
@param touchEvent An event object encapsulating the information specific to the user event.
@result YES if the receiver should respond continuously (respond when touch is dragged); NO otherwise.
*/
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent continuously to the control as it tracks a touch within the control's bounds.
@param touch The touch on the receiving control.
@param touchEvent An event object encapsulating the information specific to the user event.
@result YES if touch tracking should continue; NO otherwise.
*/
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent to the control when tracking should be cancelled.
@param touchEvent An event object encapsulating the information specific to the user event. This parameter may be nil, indicating that the cancelation was caused by something other than an event, such as the display node being removed from its supernode.
*/
- (void)cancelTrackingWithEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Sent to the control when the last touch completely ends, telling it to stop tracking.
@param touch The touch that ended.
@param touchEvent An event object encapsulating the information specific to the user event.
*/
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)touchEvent;
/**
@abstract Settable version of highlighted property.
*/
@property (getter=isHighlighted) BOOL highlighted;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,150 @@
//
// ASControlNode.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#pragma once
NS_ASSUME_NONNULL_BEGIN
/**
@abstract Kinds of events possible for control nodes.
@discussion These events are identical to their UIControl counterparts.
*/
typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
{
/** A touch-down event in the control node. */
ASControlNodeEventTouchDown = 1 << 0,
/** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */
ASControlNodeEventTouchDownRepeat = 1 << 1,
/** An event where a finger is dragged inside the bounds of the control node. */
ASControlNodeEventTouchDragInside = 1 << 2,
/** An event where a finger is dragged just outside the bounds of the control. */
ASControlNodeEventTouchDragOutside = 1 << 3,
/** A touch-up event in the control node where the finger is inside the bounds of the node. */
ASControlNodeEventTouchUpInside = 1 << 4,
/** A touch-up event in the control node where the finger is outside the bounds of the node. */
ASControlNodeEventTouchUpOutside = 1 << 5,
/** A system event canceling the current touches for the control node. */
ASControlNodeEventTouchCancel = 1 << 6,
/** A system event triggered when controls like switches, slides, etc change state. */
ASControlNodeEventValueChanged = 1 << 12,
/** A system event when the Play/Pause button on the Apple TV remote is pressed. */
ASControlNodeEventPrimaryActionTriggered = 1 << 13,
/** All events, including system events. */
ASControlNodeEventAllEvents = 0xFFFFFFFF
};
/**
* Compatibility aliases for @c ASControlState enum.
* We previously provided our own enum, but when it was imported
* into Swift, the @c normal (0) option disappeared.
*
* Apple's UIControlState enum gets special treatment here, and
* UIControlStateNormal is available in Swift.
*/
typedef UIControlState ASControlState ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlState.");
static UIControlState const ASControlStateNormal ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateNormal.") = UIControlStateNormal;
static UIControlState const ASControlStateDisabled ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateDisabled.") = UIControlStateDisabled;
static UIControlState const ASControlStateHighlighted ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateHighlighted.") = UIControlStateHighlighted;
static UIControlState const ASControlStateSelected ASDISPLAYNODE_DEPRECATED_MSG("Use UIControlStateSelected.") = UIControlStateSelected;
/**
@abstract ASControlNode is the base class for control nodes (such as buttons), or nodes that track touches to invoke targets with action messages.
@discussion ASControlNode cannot be used directly. It instead defines the common interface and behavior structure for all its subclasses. Subclasses should import "ASControlNode+Subclasses.h" for information on methods intended to be overriden.
*/
@interface ASControlNode : ASDisplayNode
#pragma mark - Control State
/**
@abstract Indicates whether or not the receiver is enabled.
@discussion Specify YES to make the control enabled; otherwise, specify NO to make it disabled. The default value is YES. If the enabled state is NO, the control ignores touch events and subclasses may draw differently.
*/
@property (getter=isEnabled) BOOL enabled;
/**
@abstract Indicates whether or not the receiver is highlighted.
@discussion This is set automatically when the there is a touch inside the control and removed on exit or touch up. This is different from touchInside in that it includes an area around the control, rather than just for touches inside the control.
*/
@property (getter=isHighlighted) BOOL highlighted;
/**
@abstract Indicates whether or not the receiver is highlighted.
@discussion This is set automatically when the receiver is tapped.
*/
@property (getter=isSelected) BOOL selected;
#pragma mark - Tracking Touches
/**
@abstract Indicates whether or not the receiver is currently tracking touches related to an event.
@discussion YES if the receiver is tracking touches; NO otherwise.
*/
@property (readonly, getter=isTracking) BOOL tracking;
/**
@abstract Indicates whether or not a touch is inside the bounds of the receiver.
@discussion YES if a touch is inside the receiver's bounds; NO otherwise.
*/
@property (readonly, getter=isTouchInside) BOOL touchInside;
#pragma mark - Action Messages
/**
@abstract Adds a target-action pair for a particular event (or events).
@param target The object to which the action message is sent. If this is nil, the responder chain is searched for an object willing to respond to the action message. target is not retained.
@param action A selector identifying an action message. May optionally include the sender and the event as parameters, in that order. May not be NULL.
@param controlEvents A bitmask specifying the control events for which the action message is sent. May not be 0. See "Control Events" for bitmask constants.
@discussion You may call this method multiple times, and you may specify multiple target-action pairs for a particular event. Targets are held weakly.
*/
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
/**
@abstract Returns the actions that are associated with a target and a particular control event.
@param target The target object. May not be nil.
@param controlEvent A single constant of type ASControlNodeEvent that specifies a particular user action on the control; for a list of these constants, see "Control Events". May not be 0 or ASControlNodeEventAllEvents.
@result An array of selector names as NSString objects, or nil if there are no action selectors associated with controlEvent.
*/
- (nullable NSArray<NSString *> *)actionsForTarget:(id)target forControlEvent:(ASControlNodeEvent)controlEvent AS_WARN_UNUSED_RESULT;
/**
@abstract Returns all target objects associated with the receiver.
@result A set of all targets for the receiver. The set may include NSNull to indicate at least one nil target (meaning, the responder chain is searched for a target.)
*/
- (NSSet *)allTargets AS_WARN_UNUSED_RESULT;
/**
@abstract Removes a target-action pair for a particular event.
@param target The target object. Pass nil to remove all targets paired with action and the specified control events.
@param action A selector identifying an action message. Pass NULL to remove all action messages paired with target.
@param controlEvents A bitmask specifying the control events associated with target and action. See "Control Events" for bitmask constants. May not be 0.
*/
- (void)removeTarget:(nullable id)target action:(nullable SEL)action forControlEvents:(ASControlNodeEvent)controlEvents;
/**
@abstract Sends the actions for the control events for a particular event.
@param controlEvents A bitmask specifying the control events for which to send actions. See "Control Events" for bitmask constants. May not be 0.
@param event The event which triggered these control actions. May be nil.
*/
- (void)sendActionsForControlEvents:(ASControlNodeEvent)controlEvents withEvent:(nullable UIEvent *)event;
@end
#if TARGET_OS_TV
@interface ASControlNode (tvOS)
/**
@abstract How the node looks when it isn't focused. Exposed here so that subclasses can override.
*/
- (void)setDefaultFocusAppearance;
@end
#endif
NS_ASSUME_NONNULL_END
@@ -0,0 +1,33 @@
//
// ASControlTargetAction.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/**
@abstract ASControlTargetAction stores target action pairs registered for specific ASControlNodeEvent values.
*/
@interface ASControlTargetAction : NSObject
/**
The action to be called on the registered target.
*/
@property (nonatomic) SEL action;
/**
Event handler target. The specified action will be called on this object.
*/
@property (nonatomic, weak) id target;
/**
Indicated whether this target was created without a target, so the action should travel up in the responder chain.
*/
@property (nonatomic, readonly) BOOL createdWithNoTarget;
@end
@@ -0,0 +1,317 @@
//
// ASDimension.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
//
#pragma once
#import <UIKit/UIKit.h>
#import <UIKit/UIGeometry.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASAssert.h>
NS_ASSUME_NONNULL_BEGIN
#pragma mark -
ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASPointsValidForLayout(CGFloat points)
{
return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < (CGFLOAT_MAX / 2.0));
}
ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASIsCGSizeValidForLayout(CGSize size)
{
return (ASPointsValidForLayout(size.width) && ASPointsValidForLayout(size.height));
}
// Note we want YGUndefined (10E20) to be considered invalid, so we have picked a smaller number than CGFLOAT_MAX/2.0
ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASPointsValidForSize(CGFloat points)
{
return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < 10000000.0);
}
ASDISPLAYNODE_INLINE BOOL AS_WARN_UNUSED_RESULT ASIsCGSizeValidForSize(CGSize size)
{
return (ASPointsValidForSize(size.width) && ASPointsValidForSize(size.height));
}
// Note we want YGUndefined (10E20) to be considered invalid, so we have picked a smaller number than CGFLOAT_MAX/2.0
ASDISPLAYNODE_INLINE BOOL ASIsCGPositionPointsValidForLayout(CGFloat points)
{
return ((isnormal(points) || points == 0.0) && points < 10000000.0);
}
ASDISPLAYNODE_INLINE BOOL ASIsCGPositionValidForLayout(CGPoint point)
{
return (ASIsCGPositionPointsValidForLayout(point.x) && ASIsCGPositionPointsValidForLayout(point.y));
}
ASDISPLAYNODE_INLINE BOOL ASIsCGRectValidForLayout(CGRect rect)
{
return (ASIsCGPositionValidForLayout(rect.origin) && ASIsCGSizeValidForLayout(rect.size));
}
#pragma mark - ASDimension
/**
* A dimension relative to constraints to be provided in the future.
* A ASDimension can be one of three types:
*
* "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.
*
* "Points" - Just a number. It will always resolve to exactly this amount.
*
* "Percent" - Multiplied to a provided parent amount to resolve a final amount.
*/
typedef NS_ENUM(NSInteger, ASDimensionUnit) {
/** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */
ASDimensionUnitAuto,
/** Just a number. It will always resolve to exactly this amount. This is the default type. */
ASDimensionUnitPoints,
/** Multiplied to a provided parent amount to resolve a final amount. */
ASDimensionUnitFraction,
};
typedef struct {
ASDimensionUnit unit;
CGFloat value;
} ASDimension;
/**
* Represents auto as ASDimension
*/
AS_EXTERN ASDimension const ASDimensionAuto;
/**
* Returns a dimension with the specified type and value.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value)
{
if (unit == ASDimensionUnitAuto ) {
ASDisplayNodeCAssert(value == 0, @"ASDimension auto value must be 0.");
} else if (unit == ASDimensionUnitPoints) {
ASDisplayNodeCAssertPositiveReal(@"Points", value);
} else if (unit == ASDimensionUnitFraction) {
ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", value);
}
ASDimension dimension;
dimension.unit = unit;
dimension.value = value;
return dimension;
}
/**
* Returns a dimension with the specified points value.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMake(CGFloat points)
{
return ASDimensionMake(ASDimensionUnitPoints, points);
}
/**
* Returns a dimension by parsing the specified dimension string.
* Examples: ASDimensionMake(@"50%") = ASDimensionMake(ASDimensionUnitFraction, 0.5)
* ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5)
*/
ASOVERLOADABLE AS_WARN_UNUSED_RESULT AS_EXTERN ASDimension ASDimensionMake(NSString *dimension);
/**
* Returns a dimension with the specified points value.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMakeWithPoints(CGFloat points)
{
ASDisplayNodeCAssertPositiveReal(@"Points", points);
return ASDimensionMake(ASDimensionUnitPoints, points);
}
/**
* Returns a dimension with the specified fraction value.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASDimension ASDimensionMakeWithFraction(CGFloat fraction)
{
ASDisplayNodeCAssert( 0 <= fraction && fraction <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", fraction);
return ASDimensionMake(ASDimensionUnitFraction, fraction);
}
/**
* Returns whether two dimensions are equal.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASDimensionEqualToDimension(ASDimension lhs, ASDimension rhs)
{
return (lhs.unit == rhs.unit && lhs.value == rhs.value);
}
/**
* Returns a NSString representation of a dimension.
*/
AS_EXTERN AS_WARN_UNUSED_RESULT NSString *NSStringFromASDimension(ASDimension dimension);
/**
* Resolve this dimension to a parent size.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGFloat ASDimensionResolve(ASDimension dimension, CGFloat parentSize, CGFloat autoSize)
{
switch (dimension.unit) {
case ASDimensionUnitAuto:
return autoSize;
case ASDimensionUnitPoints:
return dimension.value;
case ASDimensionUnitFraction:
return dimension.value * parentSize;
}
}
#pragma mark - ASLayoutSize
/**
* Expresses a size with relative dimensions. Only used for calculations internally in ASDimension.h
*/
typedef struct {
ASDimension width;
ASDimension height;
} ASLayoutSize;
AS_EXTERN ASLayoutSize const ASLayoutSizeAuto;
/*
* Creates an ASLayoutSize with provided min and max dimensions.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutSize ASLayoutSizeMake(ASDimension width, ASDimension height)
{
ASLayoutSize size;
size.width = width;
size.height = height;
return size;
}
/**
* Resolve this relative size relative to a parent size.
*/
ASDISPLAYNODE_INLINE CGSize ASLayoutSizeResolveSize(ASLayoutSize layoutSize, CGSize parentSize, CGSize autoSize)
{
return CGSizeMake(ASDimensionResolve(layoutSize.width, parentSize.width, autoSize.width),
ASDimensionResolve(layoutSize.height, parentSize.height, autoSize.height));
}
/*
* Returns a string representation of a relative size.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutSize(ASLayoutSize size)
{
return [NSString stringWithFormat:@"{%@, %@}",
NSStringFromASDimension(size.width),
NSStringFromASDimension(size.height)];
}
#pragma mark - ASSizeRange
/**
* Expresses an inclusive range of sizes. Used to provide a simple constraint to layout.
*/
typedef struct {
CGSize min;
CGSize max;
} ASSizeRange;
/**
* A size range with all dimensions zero.
*/
AS_EXTERN ASSizeRange const ASSizeRangeZero;
/**
* A size range from zero to infinity in both directions.
*/
AS_EXTERN ASSizeRange const ASSizeRangeUnconstrained;
/**
* Returns whether a size range has > 0.1 max width and max height.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeHasSignificantArea(ASSizeRange sizeRange)
{
static CGFloat const limit = 0.1f;
return (sizeRange.max.width > limit && sizeRange.max.height > limit);
}
/**
* Creates an ASSizeRange with provided min and max size.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
{
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height);
ASDisplayNodeCAssert(min.width <= max.width,
@"Range min width (%f) must not be larger than max width (%f).", min.width, max.width);
ASDisplayNodeCAssert(min.height <= max.height,
@"Range min height (%f) must not be larger than max height (%f).", min.height, max.height);
ASSizeRange sizeRange;
sizeRange.min = min;
sizeRange.max = max;
return sizeRange;
}
/**
* Creates an ASSizeRange with provided size as both min and max.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeMake(CGSize exactSize)
{
return ASSizeRangeMake(exactSize, exactSize);
}
/**
* Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size)
{
return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)),
MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height)));
}
/**
* Intersects another size range. If the other size range does not overlap in either dimension, this size range
* "wins" by returning a single point within its own range that is closest to the non-overlapping range.
*/
AS_EXTERN AS_WARN_UNUSED_RESULT ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange);
/**
* Returns whether two size ranges are equal in min and max size.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs)
{
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
}
/**
* Returns a string representation of a size range
*/
AS_EXTERN AS_WARN_UNUSED_RESULT NSString *NSStringFromASSizeRange(ASSizeRange sizeRange);
#if YOGA
#pragma mark - ASEdgeInsets
typedef struct {
ASDimension top;
ASDimension left;
ASDimension bottom;
ASDimension right;
ASDimension start;
ASDimension end;
ASDimension horizontal;
ASDimension vertical;
ASDimension all;
} ASEdgeInsets;
AS_EXTERN ASEdgeInsets const ASEdgeInsetsZero;
AS_EXTERN ASEdgeInsets ASEdgeInsetsMake(UIEdgeInsets edgeInsets);
#endif
NS_ASSUME_NONNULL_END
@@ -0,0 +1,102 @@
//
// ASDimensionInternal.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASLayoutElementSize
/**
* A struct specifying a ASLayoutElement's size. Example:
*
* ASLayoutElementSize size = (ASLayoutElementSize){
* .width = ASDimensionMakeWithFraction(0.25),
* .maxWidth = ASDimensionMakeWithPoints(200),
* .minHeight = ASDimensionMakeWithFraction(0.50)
* };
*
* Description: <ASLayoutElementSize: exact={25%, Auto}, min={Auto, 50%}, max={200pt, Auto}>
*
*/
typedef struct {
ASDimension width;
ASDimension height;
ASDimension minWidth;
ASDimension maxWidth;
ASDimension minHeight;
ASDimension maxHeight;
} ASLayoutElementSize;
/**
* Returns an ASLayoutElementSize with default values.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMake()
{
return (ASLayoutElementSize){
.width = ASDimensionAuto,
.height = ASDimensionAuto,
.minWidth = ASDimensionAuto,
.maxWidth = ASDimensionAuto,
.minHeight = ASDimensionAuto,
.maxHeight = ASDimensionAuto
};
}
/**
* Returns an ASLayoutElementSize with the specified CGSize values as width and height.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASLayoutElementSize ASLayoutElementSizeMakeFromCGSize(CGSize size)
{
ASLayoutElementSize s = ASLayoutElementSizeMake();
s.width = ASDimensionMakeWithPoints(size.width);
s.height = ASDimensionMakeWithPoints(size.height);
return s;
}
/**
* Returns whether two sizes are equal.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASLayoutElementSizeEqualToLayoutElementSize(ASLayoutElementSize lhs, ASLayoutElementSize rhs)
{
return (ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height)
&& ASDimensionEqualToDimension(lhs.minWidth, rhs.minWidth)
&& ASDimensionEqualToDimension(lhs.maxWidth, rhs.maxWidth)
&& ASDimensionEqualToDimension(lhs.minHeight, rhs.minHeight)
&& ASDimensionEqualToDimension(lhs.maxHeight, rhs.maxHeight));
}
/**
* Returns a string formatted to contain the data from an ASLayoutElementSize.
*/
AS_EXTERN AS_WARN_UNUSED_RESULT NSString *NSStringFromASLayoutElementSize(ASLayoutElementSize size);
/**
* Resolve the given size relative to a parent size and an auto size.
* From the given size uses width, height to resolve the exact size constraint, uses the minHeight and minWidth to
* resolve the min size constraint and the maxHeight and maxWidth to resolve the max size constraint. For every
* dimension with unit ASDimensionUnitAuto the given autoASSizeRange value will be used.
* Based on the calculated exact, min and max size constraints the final size range will be calculated.
*/
AS_EXTERN AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolveAutoSize(ASLayoutElementSize size, const CGSize parentSize, ASSizeRange autoASSizeRange);
/**
* Resolve the given size to a parent size. Uses internally ASLayoutElementSizeResolveAutoSize with {INFINITY, INFINITY} as
* as autoASSizeRange. For more information look at ASLayoutElementSizeResolveAutoSize.
*/
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT ASSizeRange ASLayoutElementSizeResolve(ASLayoutElementSize size, const CGSize parentSize)
{
return ASLayoutElementSizeResolveAutoSize(size, parentSize, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));
}
NS_ASSUME_NONNULL_END
@@ -0,0 +1,56 @@
//
// ASDisplayNode+Ancestry.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASDisplayNode (Ancestry)
/**
* Returns an object to enumerate the supernode ancestry of this node, starting with its supernode.
*
* For instance, you could write:
* for (ASDisplayNode *node in self.supernodes) {
* if ([node.backgroundColor isEqual:[UIColor blueColor]]) {
* node.hidden = YES;
* }
* }
*
* Note: If this property is read on the main thread, the enumeration will attempt to go up
* the layer hierarchy if it finds a break in the display node hierarchy.
*/
@property (readonly) id<NSFastEnumeration> supernodes;
/**
* Same as `supernodes` but begins the enumeration with self.
*/
@property (readonly) id<NSFastEnumeration> supernodesIncludingSelf;
/**
* Searches the supernodes of this node for one matching the given class.
*
* @param supernodeClass The class of node you're looking for.
* @param includeSelf Whether to include self in the search.
* @return A node of the given class that is an ancestor of this node, or nil.
*
* @note See the documentation on `supernodes` for details about the upward traversal.
*/
- (nullable __kindof ASDisplayNode *)supernodeOfClass:(Class)supernodeClass includingSelf:(BOOL)includeSelf;
/**
* e.g. "(<MYTextNode: 0xFFFF>, <MYTextContainingNode: 0xFFFF>, <MYCellNode: 0xFFFF>)"
*/
@property (copy, readonly) NSString *ancestryDescription;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,175 @@
//
// ASDisplayNode+Beta.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
AS_EXTERN void ASPerformBlockOnMainThread(void (^block)(void));
AS_EXTERN void ASPerformBlockOnBackgroundThread(void (^block)(void)); // DISPATCH_QUEUE_PRIORITY_DEFAULT
#if ASEVENTLOG_ENABLE
#define ASDisplayNodeLogEvent(node, ...) [node.eventLog logEventWithBacktrace:(AS_SAVE_EVENT_BACKTRACES ? [NSThread callStackSymbols] : nil) format:__VA_ARGS__]
#else
#define ASDisplayNodeLogEvent(node, ...)
#endif
#if ASEVENTLOG_ENABLE
#define ASDisplayNodeGetEventLog(node) node.eventLog
#else
#define ASDisplayNodeGetEventLog(node) nil
#endif
/**
* Bitmask to indicate what performance measurements the cell should record.
*/
typedef NS_OPTIONS(NSUInteger, ASDisplayNodePerformanceMeasurementOptions) {
ASDisplayNodePerformanceMeasurementOptionLayoutSpec = 1 << 0,
ASDisplayNodePerformanceMeasurementOptionLayoutComputation = 1 << 1
};
typedef struct {
CFTimeInterval layoutSpecTotalTime;
NSInteger layoutSpecNumberOfPasses;
CFTimeInterval layoutComputationTotalTime;
NSInteger layoutComputationNumberOfPasses;
} ASDisplayNodePerformanceMeasurements;
@interface ASDisplayNode (Beta)
/**
* ASTableView and ASCollectionView now throw exceptions on invalid updates
* like their UIKit counterparts. If YES, these classes will log messages
* on invalid updates rather than throwing exceptions.
*
* Note that even if AsyncDisplayKit's exception is suppressed, the app may still crash
* as it proceeds with an invalid update.
*
* This property defaults to NO. It will be removed in a future release.
*/
+ (BOOL)suppressesInvalidCollectionUpdateExceptions AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Collection update exceptions are thrown if assertions are enabled.");
+ (void)setSuppressesInvalidCollectionUpdateExceptions:(BOOL)suppresses;
/**
* @abstract Recursively ensures node and all subnodes are displayed.
* @see Full documentation in ASDisplayNode+FrameworkPrivate.h
*/
- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously;
/**
* @abstract allow modification of a context before the node's content is drawn
*
* @discussion Set the block to be called after the context has been created and before the node's content is drawn.
* You can override this to modify the context before the content is drawn. You are responsible for saving and
* restoring context if necessary. Restoring can be done in contextDidDisplayNodeContent
* This block can be called from *any* thread and it is unsafe to access any UIKit main thread properties from it.
*/
@property (nullable) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext;
/**
* @abstract allow modification of a context after the node's content is drawn
*/
@property (nullable) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
/**
* @abstract A bitmask representing which actions (layout spec, layout generation) should be measured.
*/
@property ASDisplayNodePerformanceMeasurementOptions measurementOptions;
/**
* @abstract A simple struct representing performance measurements collected.
*/
@property (readonly) ASDisplayNodePerformanceMeasurements performanceMeasurements;
#if ASEVENTLOG_ENABLE
/*
* @abstract The primitive event tracing object. You shouldn't directly use it to log event. Use the ASDisplayNodeLogEvent macro instead.
*/
@property (nonatomic, readonly) ASEventLog *eventLog;
#endif
/**
* @abstract Whether this node acts as an accessibility container. If set to YES, then this node's accessibility label will represent
* an aggregation of all child nodes' accessibility labels. Nodes in this node's subtree that are also accessibility containers will
* not be included in this aggregation, and will be exposed as separate accessibility elements to UIKit.
*/
@property BOOL isAccessibilityContainer;
/**
* @abstract Returns the default accessibility property values set by Texture on this node. For
* example, the default accessibility label for a text node may be its text content, while most
* other nodes would have nil default labels.
*/
@property (nullable, readonly, copy) NSString *defaultAccessibilityLabel;
@property (nullable, readonly, copy) NSString *defaultAccessibilityHint;
@property (nullable, readonly, copy) NSString *defaultAccessibilityValue;
@property (nullable, readonly, copy) NSString *defaultAccessibilityIdentifier;
@property (readonly) UIAccessibilityTraits defaultAccessibilityTraits;
/**
* @abstract Invoked when a user performs a custom action on an accessible node. Nodes that are children of accessibility containers, have
* an accessibity label and have an interactive UIAccessibilityTrait will automatically receive custom-action handling.
*
* @return Return a boolean value that determine whether to propagate through the responder chain.
* To halt propagation, return YES; otherwise, return NO.
*/
- (BOOL)performAccessibilityCustomAction:(UIAccessibilityCustomAction *)action;
/**
* @abstract Currently used by ASNetworkImageNode and ASMultiplexImageNode to allow their placeholders to stay if they are loading an image from the network.
* Otherwise, a display pass is scheduled and completes, but does not actually draw anything - and ASDisplayNode considers the element finished.
*/
- (BOOL)placeholderShouldPersist AS_WARN_UNUSED_RESULT;
/**
* @abstract Indicates that the receiver and all subnodes have finished displaying. 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.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)hierarchyDisplayDidFinish NS_REQUIRES_SUPER;
/**
* Only called on the root during yoga layout.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)willCalculateLayout:(ASSizeRange)constrainedSize NS_REQUIRES_SUPER;
/**
* @abstract Whether to draw all descendent nodes' contents into this node's layer's backing store.
*
* @discussion
* When called, causes all descendent nodes' contents to be drawn directly into this node's layer's backing
* store.
*
* If a node's descendants are static (never animated or never change attributes after creation) then that node is a
* good candidate for rasterization. Rasterizing descendants has two main benefits:
* 1) Backing stores for descendant layers are not created. Instead the layers are drawn directly into the rasterized
* container. This can save a great deal of memory.
* 2) Since the entire subtree is drawn into one backing store, compositing and blending are eliminated in that subtree
* which can help improve animation/scrolling/etc performance.
*
* Rasterization does not currently support descendants with transform, sublayerTransform, or alpha. Those properties
* will be ignored when rasterizing descendants.
*
* Note: this has nothing to do with -[CALayer shouldRasterize], which doesn't work with ASDisplayNode's asynchronous
* rendering model.
*
* Note: You cannot add subnodes whose layers/views are already loaded to a rasterized node.
* Note: You cannot call this method after the receiver's layer/view is loaded.
*/
- (void)enableSubtreeRasterization;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,29 @@
//
// ASDisplayNode+Convenience.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
NS_ASSUME_NONNULL_BEGIN
@class UIViewController;
@interface ASDisplayNode (Convenience)
/**
* @abstract Returns the view controller nearest to this node in the view hierarchy.
*
* @warning This property may only be accessed on the main thread. This property may
* be @c nil until the node's view is actually hosted in the view hierarchy.
*/
@property (nonatomic, nullable, readonly) __kindof UIViewController *closestViewController;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,320 @@
//
// ASDisplayNode+FrameworkPrivate.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 <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASInterfaceStateDelegate;
/**
Hierarchy state is propagated from nodes to all of their children when certain behaviors are required from the subtree.
Examples include rasterization and external driving of the .interfaceState property.
By passing this information explicitly, performance is optimized by avoiding iteration up the supernode chain.
Lastly, this avoidance of supernode traversal protects against the possibility of deadlocks when a supernode is
simultaneously attempting to materialize views / layers for its subtree (as many related methods require property locking)
Note: as the hierarchy deepens, more state properties may be enabled. However, state properties may never be disabled /
cancelled below the point they are enabled. They continue to the leaves of the hierarchy.
*/
typedef NS_OPTIONS(NSUInteger, ASHierarchyState)
{
/** The node may or may not have a supernode, but no supernode has a special hierarchy-influencing option enabled. */
ASHierarchyStateNormal = 0,
/** The node has a supernode with .rasterizesSubtree = YES.
Note: the root node of the rasterized subtree (the one with the property set on it) will NOT have this state set. */
ASHierarchyStateRasterized = 1 << 0,
/** The node or one of its supernodes is managed by a class like ASRangeController. Most commonly, these nodes are
ASCellNode objects or a subnode of one, and are used in ASTableView or ASCollectionView.
These nodes also receive regular updates to the .interfaceState property with more detailed status information. */
ASHierarchyStateRangeManaged = 1 << 1,
/** Down-propagated version of _flags.visibilityNotificationsDisabled. This flag is very rarely set, but by having it
locally available to nodes, they do not have to walk up supernodes at the critical points it is checked. */
ASHierarchyStateTransitioningSupernodes = 1 << 2,
/** One of the supernodes of this node is performing a transition.
Any layout calculated during this state should not be applied immediately, but pending until later. */
ASHierarchyStateLayoutPending = 1 << 3,
};
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesLayoutPending(ASHierarchyState hierarchyState)
{
return ((hierarchyState & ASHierarchyStateLayoutPending) == ASHierarchyStateLayoutPending);
}
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRangeManaged(ASHierarchyState hierarchyState)
{
return ((hierarchyState & ASHierarchyStateRangeManaged) == ASHierarchyStateRangeManaged);
}
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesRasterized(ASHierarchyState hierarchyState)
{
return ((hierarchyState & ASHierarchyStateRasterized) == ASHierarchyStateRasterized);
}
ASDISPLAYNODE_INLINE BOOL ASHierarchyStateIncludesTransitioningSupernodes(ASHierarchyState hierarchyState)
{
return ((hierarchyState & ASHierarchyStateTransitioningSupernodes) == ASHierarchyStateTransitioningSupernodes);
}
__unused static NSString * _Nonnull NSStringFromASHierarchyState(ASHierarchyState hierarchyState)
{
NSMutableArray *states = [NSMutableArray array];
if (hierarchyState == ASHierarchyStateNormal) {
[states addObject:@"Normal"];
}
if (ASHierarchyStateIncludesRangeManaged(hierarchyState)) {
[states addObject:@"RangeManaged"];
}
if (ASHierarchyStateIncludesLayoutPending(hierarchyState)) {
[states addObject:@"LayoutPending"];
}
if (ASHierarchyStateIncludesRasterized(hierarchyState)) {
[states addObject:@"Rasterized"];
}
if (ASHierarchyStateIncludesTransitioningSupernodes(hierarchyState)) {
[states addObject:@"TransitioningSupernodes"];
}
return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]];
}
#define HIERARCHY_STATE_DELTA(Name) ({ \
if ((oldState & ASHierarchyState##Name) != (newState & ASHierarchyState##Name)) { \
[changes appendFormat:@"%c%s ", (newState & ASHierarchyState##Name ? '+' : '-'), #Name]; \
} \
})
__unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarchyState oldState, ASHierarchyState newState)
{
if (oldState == newState) {
return @"{ }";
}
NSMutableString *changes = [NSMutableString stringWithString:@"{ "];
HIERARCHY_STATE_DELTA(Rasterized);
HIERARCHY_STATE_DELTA(RangeManaged);
HIERARCHY_STATE_DELTA(TransitioningSupernodes);
HIERARCHY_STATE_DELTA(LayoutPending);
[changes appendString:@"}"];
return changes;
}
#undef HIERARCHY_STATE_DELTA
@interface ASDisplayNode () <ASDescriptionProvider, ASDebugDescriptionProvider>
{
@protected
ASInterfaceState _interfaceState;
ASHierarchyState _hierarchyState;
}
// The view class to use when creating a new display node instance. Defaults to _ASDisplayView.
+ (Class)viewClass;
// Thread safe way to access the bounds of the node
@property (nonatomic) CGRect threadSafeBounds;
// Returns the bounds of the node without reaching the view or layer
- (CGRect)_locked_threadSafeBounds;
// The -pendingInterfaceState holds the value that will be applied to -interfaceState by the
// ASCATransactionQueue. If already applied, it matches -interfaceState. Thread-safe access.
@property (nonatomic, readonly) ASInterfaceState pendingInterfaceState;
// These methods are recursive, and either union or remove the provided interfaceState to all sub-elements.
- (void)enterInterfaceState:(ASInterfaceState)interfaceState;
- (void)exitInterfaceState:(ASInterfaceState)interfaceState;
- (void)recursivelySetInterfaceState:(ASInterfaceState)interfaceState;
// These methods are recursive, and either union or remove the provided hierarchyState to all sub-elements.
- (void)enterHierarchyState:(ASHierarchyState)hierarchyState;
- (void)exitHierarchyState:(ASHierarchyState)hierarchyState;
// Changed before calling willEnterHierarchy / didExitHierarchy.
@property (readonly, getter = isInHierarchy) BOOL inHierarchy;
// Call willEnterHierarchy if necessary and set inHierarchy = YES if visibility notifications are enabled on all of its parents
- (void)__enterHierarchy;
// Call didExitHierarchy if necessary and set inHierarchy = NO if visibility notifications are enabled on all of its parents
- (void)__exitHierarchy;
/**
* @abstract Returns the Hierarchy State of the node.
*
* @return The current ASHierarchyState of the node, indicating whether it is rasterized or managed by a range controller.
*
* @see ASInterfaceState
*/
@property (nonatomic) ASHierarchyState hierarchyState;
/**
* @abstract Return if the node is range managed or not
*
* @discussion Currently only set interface state on nodes in table and collection views. For other nodes, if they are
* in the hierarchy we enable all ASInterfaceState types with `ASInterfaceStateInHierarchy`, otherwise `None`.
*/
- (BOOL)supportsRangeManagedInterfaceState;
- (BOOL)_locked_displaysAsynchronously;
// The two methods below will eventually be exposed, but their names are subject to change.
/**
* @abstract Ensure that all rendering is complete for this node and its descendants.
*
* @discussion Calling this method on the main thread after a node is added to the view hierarchy will ensure that
* placeholder states are never visible to the user. It is used by ASTableView, ASCollectionView, and ASViewController
* to implement their respective ".neverShowPlaceholders" option.
*
* If all nodes have layer.contents set and/or their layer does not have -needsDisplay set, the method will return immediately.
*
* This method is capable of handling a mixed set of nodes, with some not having started display, some in progress on an
* asynchronous display operation, and some already finished.
*
* In order to guarantee against deadlocks, this method should only be called on the main thread.
* It may block on the private queue, [_ASDisplayLayer displayQueue]
*/
- (void)recursivelyEnsureDisplaySynchronously:(BOOL)synchronously;
/**
* @abstract Calls -didExitPreloadState on the receiver and its subnode hierarchy.
*
* @discussion Clears any memory-intensive preloaded content.
* This method is used to notify the node that it should purge any content that is both expensive to fetch and to
* retain in memory.
*
* @see [ASDisplayNode(Subclassing) didExitPreloadState] and [ASDisplayNode(Subclassing) didEnterPreloadState]
*/
- (void)recursivelyClearPreloadedData;
/**
* @abstract Calls -didEnterPreloadState on the receiver and its subnode hierarchy.
*
* @discussion Fetches content from remote sources for the current node and all subnodes.
*
* @see [ASDisplayNode(Subclassing) didEnterPreloadState] and [ASDisplayNode(Subclassing) didExitPreloadState]
*/
- (void)recursivelyPreload;
/**
* @abstract Triggers a recursive call to -didEnterPreloadState when the node has an interfaceState of ASInterfaceStatePreload
*/
- (void)setNeedsPreload;
/**
* @abstract Allows a node to bypass all ensureDisplay passes. Defaults to NO.
*
* @discussion Nodes that are expensive to draw and expected to have placeholder even with
* .neverShowPlaceholders enabled should set this to YES.
*
* ASImageNode uses the default of NO, as it is often used for UI images that are expected to synchronize with ensureDisplay.
*
* ASNetworkImageNode and ASMultiplexImageNode set this to YES, because they load data from a database or server,
* and are expected to support a placeholder state given that display is often blocked on slow data fetching.
*/
@property BOOL shouldBypassEnsureDisplay;
/**
* @abstract Checks whether a node should be scheduled for display, considering its current and new interface states.
*/
- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState;
/**
* @abstract safeAreaInsets will fallback to this value if the corresponding UIKit property is not available
* (due to an old iOS version).
*
* @discussion This should be set by the owning view controller based on it's layout guides.
* If this is not a view controllet's node the value will be calculated automatically by the parent node.
*/
@property (nonatomic) UIEdgeInsets fallbackSafeAreaInsets;
/**
* @abstract Indicates if this node is a view controller's root node. Defaults to NO.
*
* @discussion Set to YES in -[ASViewController initWithNode:].
*
* YES here only means that this node is used as an ASViewController node. It doesn't mean that this node is a root of
* ASDisplayNode hierarchy, e.g. when its view controller is parented by another ASViewController.
*/
@property (nonatomic, getter=isViewControllerRoot) BOOL viewControllerRoot;
@end
@interface ASDisplayNode (ASLayoutInternal)
/**
* @abstract Informs the root node that the intrinsic size of the receiver is no longer valid.
*
* @discussion The size of a root node is determined by each subnode. Calling invalidateSize will let the root node know
* that the intrinsic size of the receiver node is no longer valid and a resizing of the root node needs to happen.
*/
- (void)_u_setNeedsLayoutFromAbove;
/**
* @abstract Subclass hook for nodes that are acting as root nodes. This method is called if one of the subnodes
* size is invalidated and may need to result in a different size as the current calculated size.
*/
- (void)_rootNodeDidInvalidateSize;
/**
* This method will confirm that the layout is up to date (and update if needed).
* Importantly, it will also APPLY the layout to all of our subnodes if (unless parent is transitioning).
*/
- (void)_u_measureNodeWithBoundsIfNecessary:(CGRect)bounds;
/**
* Layout all of the subnodes based on the sublayouts
*/
- (void)_layoutSublayouts;
@end
@interface ASDisplayNode (ASLayoutTransitionInternal)
/**
* If one or multiple layout transitions are in flight this methods returns if the current layout transition that
* happens in in this particular thread was invalidated through another thread is starting a transition for this node
*/
- (BOOL)_isLayoutTransitionInvalid;
/**
* Same as @c -_isLayoutTransitionInvalid but must be called with the node's instance lock held.
*/
- (BOOL)_locked_isLayoutTransitionInvalid;
/**
* Internal method that can be overriden by subclasses to add specific behavior after the measurement of a layout
* transition did finish.
*/
- (void)_layoutTransitionMeasurementDidFinish;
/**
* Informs the node that the pending layout transition did complete
*/
- (void)_completePendingLayoutTransition;
/**
* Called if the pending layout transition did complete
*/
- (void)_pendingLayoutTransitionDidComplete;
@end
@interface ASDisplayNode (AccessibilityInternal)
- (NSArray *)accessibilityElements;
@end;
NS_ASSUME_NONNULL_END
@@ -0,0 +1,131 @@
//
// ASDisplayNode+InterfaceState.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.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>
/**
* @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;
@optional
/**
* @abstract Called when the node is about to calculate layout. This is only called before
* Yoga-driven layouts.
* @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, but implementations should be careful not
* to attempt to ascend the node tree when handling this, as the root node is locked when this is
* called.
*/
- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize;
@end
@@ -0,0 +1,67 @@
//
// ASDisplayNode+LayoutSpec.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDimension.h>
@class ASLayout;
NS_ASSUME_NONNULL_BEGIN
@interface ASDisplayNode (ASLayoutSpec)
/**
* @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
* implement layoutSpecThatFits:
*
* @return A block that takes a constrainedSize ASSizeRange argument, and must return an ASLayoutSpec that includes all
* of the subnodes to position in the layout. This input-output relationship is identical to the subclass override
* method -layoutSpecThatFits:
*
* @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger
* an exception. A future version of the framework may support using both, calling them serially, with the
* .layoutSpecBlock superseding any values set by the method override.
*
* @code ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {};
*/
@property (nullable) ASLayoutSpecBlock layoutSpecBlock;
@end
// These methods are intended to be used internally to Texture, and should not be called directly.
@interface ASDisplayNode (ASLayoutSpecPrivate)
/// For internal usage only
- (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize;
@end
@interface ASDisplayNode (ASLayoutSpecSubclasses)
/**
* @abstract Return a layout spec that describes the layout of the receiver and its children.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned layout spec
* is used to calculate an ASLayout and cached by ASDisplayNode for quick access during -layout. 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 -layoutThatFits: instead.
*
* @warning Subclasses that implement -layoutSpecThatFits: must not use .layoutSpecBlock. Doing so will trigger an
* exception. A future version of the framework may support using both, calling them serially, with the .layoutSpecBlock
* superseding any values set by the method override.
*/
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,493 @@
//
// ASDisplayNode+Subclasses.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBlockTypes.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+LayoutSpec.h>
@class ASLayoutSpec, _ASDisplayLayer;
NS_ASSUME_NONNULL_BEGIN
/**
* 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 +displayWithParameters: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 (Subclassing) <ASInterfaceStateDelegate>
#pragma mark - Properties
/** @name Properties */
/**
* @abstract Return the calculated layout.
*
* @discussion For node subclasses that implement manual layout (e.g., they have a custom -layout method),
* calculatedLayout may be accessed on subnodes to retrieved cached information about their size.
* This allows -layout to be very fast, saving time on the main thread.
* Note: .calculatedLayout will only be set for nodes that have had -layoutThatFits: called on them.
* For manual layout, make sure you call -layoutThatFits: in your implementation of -calculateSizeThatFits:.
*
* For node subclasses that use automatic layout (e.g., they implement -layoutSpecThatFits:),
* it is typically not necessary to use .calculatedLayout at any point. For these nodes,
* the ASLayoutSpec implementation will automatically call -layoutThatFits: on all of the subnodes,
* and the ASDisplayNode base class implementation of -layout will automatically make use of .calculatedLayout on the subnodes.
*
* @return Layout that wraps calculated size returned by -calculateSizeThatFits: (in manual layout mode),
* or layout already calculated from layout spec returned by -layoutSpecThatFits: (in automatic layout mode).
*
* @warning Subclasses must not override this; it returns the last cached layout and is never expensive.
*/
@property (nullable, readonly) ASLayout *calculatedLayout;
#pragma mark - View Lifecycle
/** @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.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)didLoad ASDISPLAYNODE_REQUIRES_SUPER;
/**
* An empty method that you can implement in a category to add global
* node initialization behavior. This method will be called by [ASDisplayNode init].
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)baseDidInit;
/**
* An empty method that you can implement in a category to add global
* node deallocation behavior. This method will be called by [ASDisplayNode dealloc].
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)baseWillDealloc;
#pragma mark - Layout
/** @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 ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @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.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)layoutDidFinish ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Called on a background thread if !isNodeLoaded - called on the main thread if isNodeLoaded.
*
* @discussion When the .calculatedLayout property is set to a new ASLayout (directly from -calculateLayoutThatFits: or
* calculated via use of -layoutSpecThatFits:), subclasses may inspect it here.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)calculatedLayoutDidChange ASDISPLAYNODE_REQUIRES_SUPER;
#pragma mark - Layout calculation
/** @name Layout calculation */
/**
* @abstract Calculate a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion This method is called on a non-main thread. The default implementation calls either -layoutSpecThatFits:
* or -calculateSizeThatFits:, whichever method is overriden. Subclasses rarely need to override this method,
* override -layoutSpecThatFits: or -calculateSizeThatFits: instead.
*
* @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or -calculatedLayout instead.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
/**
* ASDisplayNode's implementation of -layoutThatFits:parentSize: calls this method to resolve the node's size
* against parentSize, intersect it with constrainedSize, and call -calculateLayoutThatFits: with the result.
*
* In certain advanced cases, you may want to customize this logic. Overriding this method allows you to receive all
* three parameters and do the computation yourself.
*
* @warning Overriding this method should be done VERY rarely.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutElementSize)size
relativeToParentSize:(CGSize)parentSize;
/**
* @abstract Return the calculated size.
*
* @param constrainedSize The maximum size the receiver should fit in.
*
* @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned size
* is wrapped in an ASLayout and cached for quick access during -layout. 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 Subclasses that override are committed to manual layout. Therefore, -layout: must be overriden to layout all subnodes or subviews.
*
* @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or layoutThatFits:parentSize: instead.
*/
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize;
/**
* @abstract Invalidate previously measured and cached layout.
*
* @discussion Subclasses should call this method to invalidate the previously measured and cached layout for the display
* node, when the contents of the node change in such a way as to require measuring it again.
*/
- (void)invalidateCalculatedLayout;
#pragma mark - Observing Node State Changes
/** @name Observing node state changes */
/**
* Declare <ASInterfaceStateDelegate> methods as requiring super calls (this can't be required in the protocol).
* For descriptions, see <ASInterfaceStateDelegate> definition.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)didEnterVisibleState ASDISPLAYNODE_REQUIRES_SUPER;
AS_CATEGORY_IMPLEMENTABLE
- (void)didExitVisibleState ASDISPLAYNODE_REQUIRES_SUPER;
AS_CATEGORY_IMPLEMENTABLE
- (void)didEnterDisplayState ASDISPLAYNODE_REQUIRES_SUPER;
AS_CATEGORY_IMPLEMENTABLE
- (void)didExitDisplayState ASDISPLAYNODE_REQUIRES_SUPER;
AS_CATEGORY_IMPLEMENTABLE
- (void)didEnterPreloadState ASDISPLAYNODE_REQUIRES_SUPER;
AS_CATEGORY_IMPLEMENTABLE
- (void)didExitPreloadState ASDISPLAYNODE_REQUIRES_SUPER;
AS_CATEGORY_IMPLEMENTABLE
- (void)interfaceStateDidChange:(ASInterfaceState)newState
fromState:(ASInterfaceState)oldState ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Called when the node's ASTraitCollection changes
*
* @discussion Subclasses can override this method to react to a trait collection change.
*/
AS_CATEGORY_IMPLEMENTABLE
- (void)asyncTraitCollectionDidChange;
#pragma mark - Drawing
/** @name Drawing */
/**
* @summary Delegate method to draw layer contents into a CGBitmapContext. The current UIGraphics context will be set
* to an appropriate context.
*
* @param bounds Region to draw in.
* @param parameters An object describing all of the properties you need to draw. Return this from
* -drawParametersForAsyncLayer:
* @param isCancelledBlock 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:(nullable id)parameters
isCancelled:(AS_NOESCAPE 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 isCancelledBlock 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)
*/
+ (nullable UIImage *)displayWithParameters:(nullable id)parameters
isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock;
/**
* @abstract Delegate override for drawParameters
*
* @param layer The layer that will be drawn into.
*
* @note Called on the main thread only
*/
- (nullable id<NSObject>)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer;
/**
* @abstract Indicates that the receiver is about to display.
*
* @discussion Deprecated in 2.5.
*
* @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) is
* about to begin.
*
* @note Called on the main thread only
*/
- (void)displayWillStart ASDISPLAYNODE_REQUIRES_SUPER ASDISPLAYNODE_DEPRECATED_MSG("Use displayWillStartAsynchronously: instead.");
/**
* @abstract Indicates that the receiver is about to display.
*
* @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) is
* about to begin.
*
* @note Called on the main thread only
*/
- (void)displayWillStartAsynchronously:(BOOL)asynchronously ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Indicates that the receiver has finished displaying.
*
* @discussion Subclasses may override this method to be notified when display (asynchronous or synchronous) has
* completed.
*
* @note Called on the main thread only
*/
- (void)displayDidFinish ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Called just before the view is added to a window.
*/
- (void)willEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Called after the view is removed from the window.
*/
- (void)didExitHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* Called just after the view is added to a window.
* Note: this may be called multiple times during view controller transitions. To overcome this: use didEnterVisibleState or its equavalents.
*/
- (void)didEnterHierarchy ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Whether the view or layer of this display node is currently in a window
*/
@property (readonly, getter=isInHierarchy) BOOL inHierarchy;
/**
* Provides an opportunity to clear backing store and other memory-intensive intermediates, such as text layout managers
* on the current node.
*
* @discussion Called by -recursivelyClearContents. Always called on main thread. Base class implements self.contents = nil, clearing any backing
* store, for asynchronous regeneration when needed.
*/
- (void)clearContents ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Indicates that the receiver is about to display its subnodes. This method is not called if there are no
* subnodes present.
*
* @param subnode The subnode of which display is about to begin.
*
* @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) is
* about to begin.
*/
- (void)subnodeDisplayWillStart:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Indicates that the receiver is finished displaying its subnodes. This method is not called if there are
* no subnodes present.
*
* @param subnode The subnode of which display is about to completed.
*
* @discussion Subclasses may override this method to be notified when subnode display (asynchronous or synchronous) has
* completed.
*/
- (void)subnodeDisplayDidFinish:(ASDisplayNode *)subnode ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @abstract Marks the receiver's bounds as needing to be redrawn, with a scale value.
*
* @param contentsScale The scale at which the receiver should be drawn.
*
* @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 -setNeedsDisplay, contentsScaleForDisplay
*/
- (void)setNeedsDisplayAtScale:(CGFloat)contentsScale;
/**
* @abstract Recursively calls setNeedsDisplayAtScale: on subnodes.
*
* @param contentsScale The scale at which the receiver's subnode hierarchy should be drawn.
*
* @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;
/**
* @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 (readonly) CGFloat contentsScaleForDisplay;
#pragma mark - Touch handling
/** @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<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @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<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @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<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @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:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event ASDISPLAYNODE_REQUIRES_SUPER;
#pragma mark - Managing Gesture Recognizers
/** @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;
#pragma mark - Hit Testing
/** @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.
*/
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event;
#pragma mark - Placeholders
/** @name Placeholders */
/**
* @abstract Optionally provide an image to serve as the placeholder for the backing store while the contents are being
* displayed.
*
* @discussion
* Subclasses may override this method and return an image to use as the placeholder. Take caution as there may be a
* time and place where this method is called on a background thread. Note that -[UIImage imageNamed:] is not thread
* safe when using image assets.
*
* To retrieve the CGSize to do any image drawing, use the node's calculatedSize property.
*
* Defaults to nil.
*
* @note Called on the display queue and/or main queue (MUST BE THREAD SAFE)
*/
- (nullable UIImage *)placeholderImage;
#pragma mark - Description
/** @name Description */
/**
* @abstract Return a description of the node
*
* @discussion The function that gets called for each display node in -recursiveDescription
*/
- (NSString *)descriptionForRecursiveDescription;
@end
// Check that at most a layoutSpecBlock or one of the three layout methods is overridden
#define __ASDisplayNodeCheckForLayoutMethodOverrides \
ASDisplayNodeAssert(_layoutSpecBlock != NULL || \
((ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateSizeThatFits:)) ? 1 : 0) \
+ (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(layoutSpecThatFits:)) ? 1 : 0) \
+ (ASDisplayNodeSubclassOverridesSelector(self.class, @selector(calculateLayoutThatFits:)) ? 1 : 0)) <= 1, \
@"Subclass %@ must at least provide a layoutSpecBlock or override at most one of the three layout methods: calculateLayoutThatFits:, layoutSpecThatFits:, or calculateSizeThatFits:", NSStringFromClass(self.class))
#define ASDisplayNodeAssertThreadAffinity(viewNode) ASDisplayNodeAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
#define ASDisplayNodeCAssertThreadAffinity(viewNode) ASDisplayNodeCAssert(!viewNode || ASMainThreadAssertionsAreDisabled() || ASDisplayNodeThreadIsMain() || !(viewNode).nodeLoaded, @"Incorrect display node thread affinity - this method should not be called off the main thread after the ASDisplayNode's view or layer have been created")
NS_ASSUME_NONNULL_END
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,213 @@
//
// ASDisplayNodeExtras.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
//
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
/**
* Sets the debugName field for these nodes to the given symbol names, within the domain of "self.class"
* For instance, in `MYButtonNode` if you call `ASSetDebugNames(self.titleNode, _countNode)` the debug names
* for the nodes will be set to `MYButtonNode.titleNode` and `MYButtonNode.countNode`.
*/
#if DEBUG
#define ASSetDebugName(node, format, ...) node.debugName = [NSString stringWithFormat:format, __VA_ARGS__]
#define ASSetDebugNames(...) _ASSetDebugNames(self.class, @"" # __VA_ARGS__, __VA_ARGS__, nil)
#else
#define ASSetDebugName(node, format, ...)
#define ASSetDebugNames(...)
#endif
NS_ASSUME_NONNULL_BEGIN
/// For deallocation of objects on the main thread across multiple run loops.
AS_EXTERN void ASPerformMainThreadDeallocation(id _Nullable __strong * _Nonnull objectPtr);
// Because inline methods can't be extern'd and need to be part of the translation unit of code
// that compiles with them to actually inline, we both declare and define these in the header.
ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesVisible(ASInterfaceState interfaceState)
{
return ((interfaceState & ASInterfaceStateVisible) == ASInterfaceStateVisible);
}
ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesDisplay(ASInterfaceState interfaceState)
{
return ((interfaceState & ASInterfaceStateDisplay) == ASInterfaceStateDisplay);
}
ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesPreload(ASInterfaceState interfaceState)
{
return ((interfaceState & ASInterfaceStatePreload) == ASInterfaceStatePreload);
}
ASDISPLAYNODE_INLINE BOOL ASInterfaceStateIncludesMeasureLayout(ASInterfaceState interfaceState)
{
return ((interfaceState & ASInterfaceStateMeasureLayout) == ASInterfaceStateMeasureLayout);
}
__unused static NSString * NSStringFromASInterfaceState(ASInterfaceState interfaceState)
{
NSMutableArray *states = [NSMutableArray array];
if (interfaceState == ASInterfaceStateNone) {
[states addObject:@"No state"];
}
if (ASInterfaceStateIncludesMeasureLayout(interfaceState)) {
[states addObject:@"MeasureLayout"];
}
if (ASInterfaceStateIncludesPreload(interfaceState)) {
[states addObject:@"Preload"];
}
if (ASInterfaceStateIncludesDisplay(interfaceState)) {
[states addObject:@"Display"];
}
if (ASInterfaceStateIncludesVisible(interfaceState)) {
[states addObject:@"Visible"];
}
return [NSString stringWithFormat:@"{ %@ }", [states componentsJoinedByString:@" | "]];
}
#define INTERFACE_STATE_DELTA(Name) ({ \
if ((oldState & ASInterfaceState##Name) != (newState & ASInterfaceState##Name)) { \
[changes appendFormat:@"%c%s ", (newState & ASInterfaceState##Name ? '+' : '-'), #Name]; \
} \
})
/// e.g. { +Visible, -Preload } (although that should never actually happen.)
/// NOTE: Changes to MeasureLayout state don't really mean anything so we omit them for now.
__unused static NSString *NSStringFromASInterfaceStateChange(ASInterfaceState oldState, ASInterfaceState newState)
{
if (oldState == newState) {
return @"{ }";
}
NSMutableString *changes = [NSMutableString stringWithString:@"{ "];
INTERFACE_STATE_DELTA(Preload);
INTERFACE_STATE_DELTA(Display);
INTERFACE_STATE_DELTA(Visible);
[changes appendString:@"}"];
return changes;
}
#undef INTERFACE_STATE_DELTA
/**
Returns the appropriate interface state for a given ASDisplayNode and window
*/
AS_EXTERN ASInterfaceState ASInterfaceStateForDisplayNode(ASDisplayNode *displayNode, UIWindow *window) AS_WARN_UNUSED_RESULT;
/**
Given a layer, returns the associated display node, if any.
*/
AS_EXTERN ASDisplayNode * _Nullable ASLayerToDisplayNode(CALayer * _Nullable layer) AS_WARN_UNUSED_RESULT;
/**
Given a view, returns the associated display node, if any.
*/
AS_EXTERN ASDisplayNode * _Nullable ASViewToDisplayNode(UIView * _Nullable view) AS_WARN_UNUSED_RESULT;
/**
Given a node, returns the root of the node hierarchy (where supernode == nil)
*/
AS_EXTERN ASDisplayNode *ASDisplayNodeUltimateParentOfNode(ASDisplayNode *node) AS_WARN_UNUSED_RESULT;
/**
If traverseSublayers == YES, this function will walk the layer hierarchy, spanning discontinuous sections of the node hierarchy\
(e.g. the layers of UIKit intermediate views in UIViewControllers, UITableView, UICollectionView).
In the event that a node's backing layer is not created yet, the function will only walk the direct subnodes instead
of forcing the layer hierarchy to be created.
*/
AS_EXTERN void ASDisplayNodePerformBlockOnEveryNode(CALayer * _Nullable layer, ASDisplayNode * _Nullable node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node));
/**
This function will walk the node hierarchy in a breadth first fashion. It does run the block on the node provided
directly to the function call. It does NOT traverse sublayers.
*/
AS_EXTERN void ASDisplayNodePerformBlockOnEveryNodeBFS(ASDisplayNode *node, void(^block)(ASDisplayNode *node));
/**
Identical to ASDisplayNodePerformBlockOnEveryNode, except it does not run the block on the
node provided directly to the function call - only on all descendants.
*/
AS_EXTERN void ASDisplayNodePerformBlockOnEverySubnode(ASDisplayNode *node, BOOL traverseSublayers, void(^block)(ASDisplayNode *node));
/**
Given a display node, traverses up the layer tree hierarchy, returning the first display node that passes block.
*/
AS_EXTERN ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernode(ASDisplayNode * _Nullable node, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use the `supernodes` property instead.");
/**
Given a display node, traverses up the layer tree hierarchy, returning the first display node of kind class.
*/
AS_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT ASDISPLAYNODE_DEPRECATED_MSG("Use the `supernodeOfClass:includingSelf:` method instead.");
/**
* Given a layer, find the window it lives in, if any.
*/
AS_EXTERN UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT;
/**
* Given a layer, find the closest view it lives in, if any.
*/
AS_EXTERN UIView * _Nullable ASFindClosestViewOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT;
/**
* Given two nodes, finds their most immediate common parent. Used for geometry conversion methods.
* NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is
* disallowed in UIKit documentation and the behavior is left undefined. The output does not have a rigorously defined
* failure mode (i.e. returning CGPointZero or returning the point exactly as passed in). Rather than track the internal
* undefined and undocumented behavior of UIKit in ASDisplayNode, this operation is defined to be incorrect in all
* circumstances and must be fixed wherever encountered.
*/
AS_EXTERN ASDisplayNode * _Nullable ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2) AS_WARN_UNUSED_RESULT;
/**
Given a display node, collects all descendants. This is a specialization of ASCollectContainer() that walks the Core Animation layer tree as opposed to the display node tree, thus supporting non-continues display node hierarchies.
*/
AS_EXTERN NSArray<ASDisplayNode *> *ASCollectDisplayNodes(ASDisplayNode *node) AS_WARN_UNUSED_RESULT;
/**
Given a display node, traverses down the node hierarchy, returning all the display nodes that pass the block.
*/
AS_EXTERN NSArray<ASDisplayNode *> *ASDisplayNodeFindAllSubnodes(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT;
/**
Given a display node, traverses down the node hierarchy, returning all the display nodes of kind class.
*/
AS_EXTERN NSArray<__kindof ASDisplayNode *> *ASDisplayNodeFindAllSubnodesOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT;
/**
Given a display node, traverses down the node hierarchy, returning the depth-first display node, including the start node that pass the block.
*/
AS_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstNode(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT;
/**
Given a display node, traverses down the node hierarchy, returning the depth-first display node, excluding the start node, that pass the block
*/
AS_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSubnode(ASDisplayNode *start, BOOL (^block)(ASDisplayNode *node)) AS_WARN_UNUSED_RESULT;
/**
Given a display node, traverses down the node hierarchy, returning the depth-first display node of kind class.
*/
AS_EXTERN __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSubnodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT;
AS_EXTERN UIColor *ASDisplayNodeDefaultPlaceholderColor(void) AS_WARN_UNUSED_RESULT;
AS_EXTERN UIColor *ASDisplayNodeDefaultTintColor(void) AS_WARN_UNUSED_RESULT;
/**
Disable willAppear / didAppear / didDisappear notifications for a sub-hierarchy, then re-enable when done. Nested calls are supported.
*/
AS_EXTERN void ASDisplayNodeDisableHierarchyNotifications(ASDisplayNode *node);
AS_EXTERN void ASDisplayNodeEnableHierarchyNotifications(ASDisplayNode *node);
// Not to be called directly.
AS_EXTERN void _ASSetDebugNames(Class owningClass, NSString *names, ASDisplayNode * _Nullable object, ...);
NS_ASSUME_NONNULL_END
@@ -0,0 +1,229 @@
//
// ASEditableTextNode.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
//
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASEditableTextNodeDelegate;
@class ASTextKitComponents;
@interface ASEditableTextNodeTargetForAction: NSObject
@property (nonatomic, strong, readonly) id _Nullable target;
- (instancetype)initWithTarget:(id _Nullable)target;
@end
/**
@abstract Implements a node that supports text editing.
@discussion Does not support layer backing.
*/
@interface ASEditableTextNode : ASDisplayNode <UITextInputTraits>
/**
* @abstract Initializes an editable text node using default TextKit components.
*
* @return An initialized ASEditableTextNode.
*/
- (instancetype)init;
/**
* @abstract Initializes an editable text node using the provided TextKit components.
*
* @param textKitComponents The TextKit stack used to render text.
* @param placeholderTextKitComponents The TextKit stack used to render placeholder text.
*
* @return An initialized ASEditableTextNode.
*/
- (instancetype)initWithTextKitComponents:(ASTextKitComponents *)textKitComponents
placeholderTextKitComponents:(ASTextKitComponents *)placeholderTextKitComponents;
//! @abstract The text node's delegate, which must conform to the <ASEditableTextNodeDelegate> protocol.
@property (nullable, weak) id <ASEditableTextNodeDelegate> delegate;
#pragma mark - Configuration
/**
@abstract Enable scrolling on the textView
@default true
*/
@property (nonatomic) BOOL scrollEnabled;
@property (nonatomic, strong) UIFont *baseFont;
/**
@abstract Access to underlying UITextView for more configuration options.
@warning This property should only be used on the main thread and should not be accessed before the editable text node's view is created.
*/
@property (nonatomic, readonly) UITextView *textView;
//! @abstract The attributes to apply to new text being entered by the user.
@property (nullable, nonatomic, copy) NSDictionary<NSString *, id> *typingAttributes;
//! @abstract The range of text currently selected. If length is zero, the range is the cursor location.
@property NSRange selectedRange;
@property (readonly) CGRect selectionRect;
#pragma mark - Placeholder
/**
@abstract Indicates if the receiver is displaying the placeholder text.
@discussion To update the placeholder, see the <attributedPlaceholderText> property.
@result YES if the placeholder is currently displayed; NO otherwise.
*/
- (BOOL)isDisplayingPlaceholder AS_WARN_UNUSED_RESULT;
/**
@abstract The styled placeholder text displayed by the text node while no text is entered
@discussion The placeholder is displayed when the user has not entered any text and the keyboard is not visible.
*/
@property (nullable, nonatomic, copy) NSAttributedString *attributedPlaceholderText;
#pragma mark - Modifying User Text
/**
@abstract The styled text displayed by the receiver.
@discussion When the placeholder is displayed (as indicated by -isDisplayingPlaceholder), this value is nil. Otherwise, this value is the attributed text the user has entered. This value can be modified regardless of whether the receiver is the first responder (and thus, editing) or not. Changing this value from nil to non-nil will result in the placeholder being hidden, and the new value being displayed.
*/
@property (nullable, nonatomic, copy) NSAttributedString *attributedText;
#pragma mark - Managing The Keyboard
//! @abstract The text input mode used by the receiver's keyboard, if it is visible. This value is undefined if the receiver is not the first responder.
@property (nonatomic, readonly) UITextInputMode *textInputMode;
/**
@abstract The textContainerInset of both the placeholder and typed textView. This value defaults to UIEdgeInsetsZero.
*/
@property (nonatomic) UIEdgeInsets textContainerInset;
/**
@abstract The maximum number of lines to display. Additional lines will require scrolling.
@default 0 (No limit)
*/
@property (nonatomic) NSUInteger maximumLinesToDisplay;
/**
@abstract Indicates whether the receiver's text view is the first responder, and thus has the keyboard visible and is prepared for editing by the user.
@result YES if the receiver's text view is the first-responder; NO otherwise.
*/
- (BOOL)isFirstResponder AS_WARN_UNUSED_RESULT;
//! @abstract Makes the receiver's text view the first responder.
- (BOOL)becomeFirstResponder;
//! @abstract Resigns the receiver's text view from first-responder status, if it has it.
- (BOOL)resignFirstResponder;
#pragma mark - Geometry
/**
@abstract Returns the frame of the given range of characters.
@param textRange A range of characters.
@discussion This method raises an exception if `textRange` is not a valid range of characters within the receiver's attributed text.
@result A CGRect that is the bounding box of the glyphs covered by the given range of characters, in the coordinate system of the receiver.
*/
- (CGRect)frameForTextRange:(NSRange)textRange AS_WARN_UNUSED_RESULT;
/**
@abstract <UITextInputTraits> properties.
*/
@property (nonatomic) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences
@property (nonatomic) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault
@property (nonatomic) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault;
@property (nonatomic) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault
@property (nonatomic) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault
@property (nonatomic) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault (See note under UIReturnKeyType enum)
@property (nonatomic) BOOL enablesReturnKeyAutomatically; // default is NO (when YES, will automatically disable return key when text widget has zero-length contents, and will automatically enable when text widget has non-zero-length contents)
@property (nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry; // default is NO
@property (nonatomic, strong) NSString * _Nullable initialPrimaryLanguage;
- (void)resetInitialPrimaryLanguage;
- (void)dropAutocorrection;
- (bool)isCurrentlyEmoji;
@end
@interface ASEditableTextNode (Unavailable)
- (instancetype)initWithLayerBlock:(ASDisplayNodeLayerBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE;
- (instancetype)initWithViewBlock:(ASDisplayNodeViewBlock)viewBlock didLoadBlock:(nullable ASDisplayNodeDidLoadBlock)didLoadBlock NS_UNAVAILABLE;
@end
#pragma mark -
/**
* The methods declared by the ASEditableTextNodeDelegate protocol allow the adopting delegate to
* respond to notifications such as began and finished editing, selection changed and text updated;
* and manage whether a specified text should be replaced.
*/
@protocol ASEditableTextNodeDelegate <NSObject>
@optional
/**
@abstract Asks the delegate if editing should begin for the text node.
@param editableTextNode An editable text node.
@discussion YES if editing should begin; NO if editing should not begin -- the default returns YES.
*/
- (BOOL)editableTextNodeShouldBeginEditing:(ASEditableTextNode *)editableTextNode;
/**
@abstract Indicates to the delegate that the text node began editing.
@param editableTextNode An editable text node.
@discussion The invocation of this method coincides with the keyboard animating to become visible.
*/
- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode;
/**
@abstract Asks the delegate whether the specified text should be replaced in the editable text node.
@param editableTextNode An editable text node.
@param range The current selection range. If the length of the range is 0, range reflects the current insertion point. If the user presses the Delete key, the length of the range is 1 and an empty string object replaces that single character.
@param text The text to insert.
@discussion YES if the old text should be replaced by the new text; NO if the replacement operation should be aborted.
@result The text node calls this method whenever the user types a new character or deletes an existing character. Implementation of this method is optional -- the default implementation returns YES.
*/
- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
/**
@abstract Indicates to the delegate that the text node's selection has changed.
@param editableTextNode An editable text node.
@param fromSelectedRange The previously selected range.
@param toSelectedRange The current selected range. Equivalent to the <selectedRange> property.
@param dueToEditing YES if the selection change was due to editing; NO otherwise.
@discussion You can access the selection of the receiver via <selectedRange>.
*/
- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing;
/**
@abstract Indicates to the delegate that the text node's text was updated.
@param editableTextNode An editable text node.
@discussion This method is called each time the user updated the text node's text. It is not called for programmatic changes made to the text via the <attributedText> property.
*/
- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode;
/**
@abstract Indicates to the delegate that the text node has finished editing.
@param editableTextNode An editable text node.
@discussion The invocation of this method coincides with the keyboard animating to become hidden.
*/
- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode;
- (BOOL)editableTextNodeShouldCopy:(ASEditableTextNode *)editableTextNode;
- (BOOL)editableTextNodeShouldPaste:(ASEditableTextNode *)editableTextNode;
- (ASEditableTextNodeTargetForAction * _Nullable)editableTextNodeTargetForAction:(SEL)action;
- (BOOL)editableTextNodeShouldReturn:(ASEditableTextNode *)editableTextNode;
- (void)editableTextNodeBackspaceWhileEmpty:(ASEditableTextNode *)editableTextNode;
- (UIMenu *)editableTextNodeMenu:(ASEditableTextNode *)editableTextNode forTextRange:(NSRange)textRange suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0));
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,21 @@
//
// ASEqualityHelpers.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
//
#import <AsyncDisplayKit/ASBaseDefines.h>
/**
@abstract Correctly equates two objects, including cases where both objects are nil. The latter is a case where `isEqual:` fails.
@param obj The first object in the comparison. Can be nil.
@param otherObj The second object in the comparison. Can be nil.
@result YES if the objects are equal, including cases where both object are nil.
*/
ASDISPLAYNODE_INLINE BOOL ASObjectIsEqual(id<NSObject> obj, id<NSObject> otherObj)
{
return obj == otherObj || [obj isEqual:otherObj];
}
@@ -0,0 +1,44 @@
//
// ASExperimentalFeatures.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A bit mask of features. Make sure to update configuration.json when you add entries.
*/
typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) {
ASExperimentalGraphicsContexts = 1 << 0, // exp_graphics_contexts
// If AS_ENABLE_TEXTNODE=0 or TextNode2 subspec is used this setting is a no op and ASTextNode2
// will be used in all cases
ASExperimentalTextNode = 1 << 1, // exp_text_node
ASExperimentalInterfaceStateCoalescing = 1 << 2, // exp_interface_state_coalesce
ASExperimentalUnfairLock = 1 << 3, // exp_unfair_lock
ASExperimentalLayerDefaults = 1 << 4, // exp_infer_layer_defaults
ASExperimentalCollectionTeardown = 1 << 5, // exp_collection_teardown
ASExperimentalFramesetterCache = 1 << 6, // exp_framesetter_cache
ASExperimentalSkipClearData = 1 << 7, // exp_skip_clear_data
ASExperimentalDidEnterPreloadSkipASMLayout = 1 << 8, // exp_did_enter_preload_skip_asm_layout
ASExperimentalDisableAccessibilityCache = 1 << 9, // exp_disable_a11y_cache
ASExperimentalDispatchApply = 1 << 10, // exp_dispatch_apply
ASExperimentalImageDownloaderPriority = 1 << 11, // exp_image_downloader_priority
ASExperimentalTextDrawing = 1 << 12, // exp_text_drawing
ASExperimentalFeatureAll = 0xFFFFFFFF
};
/// Convert flags -> name array.
AS_EXTERN NSArray<NSString *> *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags);
/// Convert name array -> flags.
AS_EXTERN ASExperimentalFeatures ASExperimentalFeaturesFromArray(NSArray<NSString *> *array);
NS_ASSUME_NONNULL_END
@@ -0,0 +1,65 @@
//
// ASGraphicsContext.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASBlockTypes.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A wrapper for the UIKit drawing APIs. If you are in ASExperimentalDrawingGlobal, and you have iOS >= 10, we will create
* a UIGraphicsRenderer with an appropriate format. Otherwise, we will use UIGraphicsBeginImageContext et al.
*
* @param size The size of the context.
* @param opaque Whether the context should be opaque or not.
* @param scale The scale of the context. 0 uses main screen scale.
* @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its
* preferred renderer format if we are using UIGraphicsImageRenderer.
* @param isCancelled An optional block for canceling the drawing before forming the image. Only takes effect under
* the legacy code path, as UIGraphicsRenderer does not support cancellation.
* @param work A block, wherein the current UIGraphics context is set based on the arguments.
*
* @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext.
*/
UIImage *ASGraphicsCreateImageWithOptions(CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, asdisplaynode_iscancelled_block_t NS_NOESCAPE _Nullable isCancelled, void (NS_NOESCAPE ^work)(void)) ASDISPLAYNODE_DEPRECATED_MSG("Use ASGraphicsCreateImageWithTraitCollectionAndOptions instead");
/**
* A wrapper for the UIKit drawing APIs. If you are in ASExperimentalDrawingGlobal, and you have iOS >= 10, we will create
* a UIGraphicsRenderer with an appropriate format. Otherwise, we will use UIGraphicsBeginImageContext et al.
*
* @param traitCollection Trait collection. The `work` block will be executed with this trait collection, so it will affect dynamic colors, etc.
* @param size The size of the context.
* @param opaque Whether the context should be opaque or not.
* @param scale The scale of the context. 0 uses main screen scale.
* @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its
* preferred renderer format if we are using UIGraphicsImageRenderer.
* @param isCancelled An optional block for canceling the drawing before forming the image.
* @param work A block, wherein the current UIGraphics context is set based on the arguments.
*
* @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext.
*/
UIImage *ASGraphicsCreateImage(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, asdisplaynode_iscancelled_block_t _Nullable NS_NOESCAPE isCancelled, void (NS_NOESCAPE ^work)(void));
/**
* A wrapper for the UIKit drawing APIs.
*
* @param traitCollection Trait collection. The `work` block will be executed with this trait collection, so it will affect dynamic colors, etc.
* @param size The size of the context.
* @param opaque Whether the context should be opaque or not.
* @param scale The scale of the context. 0 uses main screen scale.
* @param sourceImage If you are planning to render a UIImage into this context, provide it here and we will use its
* preferred renderer format if we are using UIGraphicsImageRenderer.
* @param work A block, wherein the current UIGraphics context is set based on the arguments.
*
* @return The rendered image. You can also render intermediary images using UIGraphicsGetImageFromCurrentImageContext.
*/
UIImage *ASGraphicsCreateImageWithTraitCollectionAndOptions(ASPrimitiveTraitCollection traitCollection, CGSize size, BOOL opaque, CGFloat scale, UIImage * _Nullable sourceImage, void (NS_NOESCAPE ^work)(void)) ASDISPLAYNODE_DEPRECATED_MSG("Use ASGraphicsCreateImage instead");
NS_ASSUME_NONNULL_END
@@ -0,0 +1,41 @@
//
// ASHashing.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
/**
* When std::hash is unavailable, this function will hash a bucket o' bits real fast.
* The hashing algorithm is copied from CoreFoundation's private function CFHashBytes.
* https://opensource.apple.com/source/CF/CF-1153.18/CFUtilities.c.auto.html
*
* Simple example:
* CGRect myRect = { ... };
* ASHashBytes(&myRect, sizeof(myRect));
*
* Example:
* struct {
* NSUInteger imageHash;
* CGSize size;
* } data = {
* _image.hash,
* _bounds.size
* };
* return ASHashBytes(&data, sizeof(data));
*
* @warning: If a struct has padding, any fields that are intiailized in {}
* will have garbage data for their padding, which will break this hash! Either
* use `pragma clang diagnostic warning "-Wpadded"` around your struct definition
* or manually initialize the fields of your struct (`myStruct.x = 7;` etc).
*/
AS_EXTERN NSUInteger ASHashBytes(void *bytes, size_t length);
NS_ASSUME_NONNULL_END
@@ -0,0 +1,107 @@
//
// ASInternalHelpers.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
//
#import <AsyncDisplayKit/ASAvailability.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
NS_ASSUME_NONNULL_BEGIN
AS_EXTERN void ASInitializeFrameworkMainThread(void);
AS_EXTERN BOOL ASDefaultAllowsGroupOpacity(void);
AS_EXTERN BOOL ASDefaultAllowsEdgeAntialiasing(void);
AS_EXTERN BOOL ASSubclassOverridesSelector(Class superclass, Class subclass, SEL selector);
AS_EXTERN BOOL ASSubclassOverridesClassSelector(Class superclass, Class subclass, SEL selector);
/// Replace a method from the given class with a block and returns the original method IMP
AS_EXTERN IMP ASReplaceMethodWithBlock(Class c, SEL origSEL, id block);
/// Dispatches the given block to the main queue if not already running on the main thread
AS_EXTERN void ASPerformBlockOnMainThread(void (^block)(void));
/// Dispatches the given block to a background queue with priority of DISPATCH_QUEUE_PRIORITY_DEFAULT if not already run on a background queue
AS_EXTERN void ASPerformBlockOnBackgroundThread(void (^block)(void)); // DISPATCH_QUEUE_PRIORITY_DEFAULT
/// For deallocation of objects on a background thread without GCD overhead / thread explosion
AS_EXTERN void ASPerformBackgroundDeallocation(id __strong _Nullable * _Nonnull object);
AS_EXTERN CGFloat ASScreenScale(void);
AS_EXTERN CGSize ASFloorSizeValues(CGSize s);
AS_EXTERN CGFloat ASFloorPixelValue(CGFloat f);
AS_EXTERN CGPoint ASCeilPointValues(CGPoint p);
AS_EXTERN CGSize ASCeilSizeValues(CGSize s);
AS_EXTERN CGFloat ASCeilPixelValue(CGFloat f);
AS_EXTERN CGFloat ASRoundPixelValue(CGFloat f);
AS_EXTERN Class _Nullable ASGetClassFromType(const char * _Nullable type);
ASDISPLAYNODE_INLINE BOOL ASImageAlphaInfoIsOpaque(CGImageAlphaInfo info) {
switch (info) {
case kCGImageAlphaNone:
case kCGImageAlphaNoneSkipLast:
case kCGImageAlphaNoneSkipFirst:
return YES;
default:
return NO;
}
}
/**
@summary Conditionally performs UIView geometry changes in the given block without animation.
Used primarily to circumvent UITableView forcing insertion animations when explicitly told not to via
`UITableViewRowAnimationNone`. More info: https://github.com/facebook/AsyncDisplayKit/pull/445
@param withoutAnimation Set to `YES` to perform given block without animation
@param block Perform UIView geometry changes within the passed block
*/
ASDISPLAYNODE_INLINE void ASPerformBlockWithoutAnimation(BOOL withoutAnimation, void (^block)(void)) {
if (withoutAnimation) {
[UIView performWithoutAnimation:block];
} else {
block();
}
}
ASDISPLAYNODE_INLINE void ASBoundsAndPositionForFrame(CGRect rect, CGPoint origin, CGPoint anchorPoint, CGRect *bounds, CGPoint *position)
{
*bounds = (CGRect){ origin, rect.size };
*position = CGPointMake(rect.origin.x + rect.size.width * anchorPoint.x,
rect.origin.y + rect.size.height * anchorPoint.y);
}
ASDISPLAYNODE_INLINE UIEdgeInsets ASConcatInsets(UIEdgeInsets insetsA, UIEdgeInsets insetsB)
{
insetsA.top += insetsB.top;
insetsA.left += insetsB.left;
insetsA.bottom += insetsB.bottom;
insetsA.right += insetsB.right;
return insetsA;
}
@interface NSIndexPath (ASInverseComparison)
- (NSComparisonResult)asdk_inverseCompare:(NSIndexPath *)otherIndexPath;
@end
NS_ASSUME_NONNULL_END
#ifndef AS_INITIALIZE_FRAMEWORK_MANUALLY
#define AS_INITIALIZE_FRAMEWORK_MANUALLY 0
#endif
@@ -0,0 +1,159 @@
//
// ASLayout.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
//
#pragma once
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
NS_ASSUME_NONNULL_BEGIN
AS_EXTERN CGPoint const ASPointNull; // {NAN, NAN}
AS_EXTERN BOOL ASPointIsNull(CGPoint point);
AS_EXTERN NSString *const ASThreadDictMaxConstraintSizeKey;
/**
* Safely calculates the layout of the given root layoutElement by guarding against nil nodes.
* @param rootLayoutElement The root node to calculate the layout for.
* @param sizeRange The size range to calculate the root layout within.
*/
AS_EXTERN ASLayout *ASCalculateRootLayout(id<ASLayoutElement> rootLayoutElement, const ASSizeRange sizeRange);
/**
* Safely computes the layout of the given node by guarding against nil nodes.
* @param layoutElement The layout element to calculate the layout for.
* @param sizeRange The size range to calculate the node layout within.
* @param parentSize The parent size of the node to calculate the layout for.
*/
AS_EXTERN ASLayout *ASCalculateLayout(id<ASLayoutElement>layoutElement, const ASSizeRange sizeRange, const CGSize parentSize);
/**
* A node in the layout tree that represents the size and position of the object that created it (ASLayoutElement).
*/
@interface ASLayout : NSObject
/**
* The underlying object described by this layout
*/
@property (nonatomic, weak, readonly) id<ASLayoutElement> layoutElement;
/**
* The type of ASLayoutElement that created this layout
*/
@property (nonatomic, readonly) ASLayoutElementType type;
/**
* Size of the current layout
*/
@property (nonatomic, readonly) CGSize size;
/**
* Position in parent. Default to ASPointNull.
*
* @discussion When being used as a sublayout, this property must not equal ASPointNull.
*/
@property (nonatomic, readonly) CGPoint position;
/**
* Array of ASLayouts. Each must have a valid non-null position.
*/
@property (nonatomic, copy, readonly) NSArray<ASLayout *> *sublayouts;
/**
* The frame for the given element, or CGRectNull if
* the element is not a direct descendent of this layout.
*/
- (CGRect)frameForElement:(id<ASLayoutElement>)layoutElement;
/**
* @abstract Returns a valid frame for the current layout computed with the size and position.
* @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite.
*/
@property (nonatomic, readonly) CGRect frame;
/**
* Designated initializer
*/
- (instancetype)initWithLayoutElement:(id<ASLayoutElement>)layoutElement
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts NS_DESIGNATED_INITIALIZER;
/**
* Convenience class initializer for layout construction.
*
* @param layoutElement The backing ASLayoutElement object.
* @param size The size of this layout.
* @param position The position of this layout within its parent (if available).
* @param sublayouts Sublayouts belong to the new layout.
*/
+ (instancetype)layoutWithLayoutElement:(id<ASLayoutElement>)layoutElement
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
/**
* Convenience initializer that has CGPointNull position.
* Best used by ASDisplayNode subclasses that are manually creating a layout for -calculateLayoutThatFits:,
* or for ASLayoutSpec subclasses that are referencing the "self" level in the layout tree,
* or for creating a sublayout of which the position is yet to be determined.
*
* @param layoutElement The backing ASLayoutElement object.
* @param size The size of this layout.
* @param sublayouts Sublayouts belong to the new layout.
*/
+ (instancetype)layoutWithLayoutElement:(id<ASLayoutElement>)layoutElement
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
/**
* Convenience that has CGPointNull position and no sublayouts.
* Best used for creating a layout that has no sublayouts, and is either a root one
* or a sublayout of which the position is yet to be determined.
*
* @param layoutElement The backing ASLayoutElement object.
* @param size The size of this layout.
*/
+ (instancetype)layoutWithLayoutElement:(id<ASLayoutElement>)layoutElement
size:(CGSize)size NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
/**
* Traverses the existing layout tree and generates a new tree that represents only ASDisplayNode layouts
*/
- (ASLayout *)filteredNodeLayoutTree NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)new NS_UNAVAILABLE;
@end
#pragma mark - Debugging
@interface ASLayout (Debugging)
/**
* Set to YES to tell all ASLayout instances to retain their sublayout elements. Defaults to NO.
* See `-retainSublayoutLayoutElements` to control this per-instance.
*
* Note: Weaver relies on this API.
*/
@property (class) BOOL shouldRetainSublayoutLayoutElements;
/**
* Recrusively output the description of the layout tree.
*/
- (NSString *)recursiveDescription;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,307 @@
//
// ASLayoutElement.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASLayoutElementPrivate.h>
#import <AsyncDisplayKit/ASLayoutElementExtensibility.h>
#import <AsyncDisplayKit/ASDimensionInternal.h>
#import <AsyncDisplayKit/ASStackLayoutElement.h>
#import <AsyncDisplayKit/ASAbsoluteLayoutElement.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
#import <AsyncDisplayKit/ASLocking.h>
@class ASLayout;
@class ASLayoutSpec;
@protocol ASLayoutElementStylability;
@protocol ASTraitEnvironment;
NS_ASSUME_NONNULL_BEGIN
/** A constant that indicates that the parent's size is not yet determined in a given dimension. */
AS_EXTERN CGFloat const ASLayoutElementParentDimensionUndefined;
/** A constant that indicates that the parent's size is not yet determined in either dimension. */
AS_EXTERN CGSize const ASLayoutElementParentSizeUndefined;
/** Type of ASLayoutElement */
typedef NS_ENUM(NSUInteger, ASLayoutElementType) {
ASLayoutElementTypeLayoutSpec,
ASLayoutElementTypeDisplayNode
};
#pragma mark - ASLayoutElement
/**
* The ASLayoutElement protocol declares a method for measuring the layout of an object. A layout
* is defined by an ASLayout return value, and must specify 1) the size (but not position) of the
* layoutElement object, and 2) the size and position of all of its immediate child objects. The tree
* recursion is driven by parents requesting layouts from their children in order to determine their
* size, followed by the parents setting the position of the children once the size is known
*
* The protocol also implements a "family" of LayoutElement protocols. These protocols contain layout
* options that can be used for specific layout specs. For example, ASStackLayoutSpec has options
* defining how a layoutElement should shrink or grow based upon available space.
*
* These layout options are all stored in an ASLayoutOptions class (that is defined in ASLayoutElementPrivate).
* Generally you needn't worry about the layout options class, as the layoutElement protocols allow all direct
* access to the options via convenience properties. If you are creating custom layout spec, then you can
* extend the backing layout options class to accommodate any new layout options.
*/
@protocol ASLayoutElement <ASLayoutElementExtensibility, ASTraitEnvironment, ASLayoutElementAsciiArtProtocol>
#pragma mark - Getter
/**
* @abstract Returns type of layoutElement
*/
@property (nonatomic, readonly) ASLayoutElementType layoutElementType;
/**
* @abstract A size constraint that should apply to this ASLayoutElement.
*/
@property (nonatomic, readonly) ASLayoutElementStyle *style;
/**
* @abstract Returns all children of an object which class conforms to the ASLayoutElement protocol
*/
- (nullable NSArray<id<ASLayoutElement>> *)sublayoutElements;
#pragma mark - Calculate layout
/**
* @abstract Asks the node to return a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;
/**
* Call this on children layoutElements to compute their layouts within your implementation of -calculateLayoutThatFits:.
*
* @warning You may not override this method. Override -calculateLayoutThatFits: instead.
* @warning In almost all cases, prefer the use of ASCalculateLayout in ASLayout
*
* @param constrainedSize Specifies a minimum and maximum size. The receiver must choose a size that is in this range.
* @param parentSize The parent node's size. If the parent component does not have a final size in a given dimension,
* then it should be passed as ASLayoutElementParentDimensionUndefined (for example, if the parent's width
* depends on the child's size).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*/
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize;
/**
* Override this method to compute your layoutElement's layout.
*
* @discussion Why do you need to override -calculateLayoutThatFits: instead of -layoutThatFits:parentSize:?
* The base implementation of -layoutThatFits:parentSize: does the following for you:
* 1. First, it uses the parentSize parameter to resolve the nodes's size (the one assigned to the size property).
* 2. Then, it intersects the resolved size with the constrainedSize parameter. If the two don't intersect,
* constrainedSize wins. This allows a component to always override its childrens' sizes when computing its layout.
* (The analogy for UIView: you might return a certain size from -sizeThatFits:, but a parent view can always override
* that size and set your frame to any size.)
* 3. It caches it result for reuse
*
* @param constrainedSize A min and max size. This is computed as described in the description. The ASLayout you
* return MUST have a size between these two sizes.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
/**
* In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the
* layoutElement's size, parentSize, and constrained size. With these values you could calculate the final constrained size
* and call -calculateLayoutThatFits: with the result.
*
* @warning Overriding this method should be done VERY rarely.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutElementSize)size
relativeToParentSize:(CGSize)parentSize;
- (BOOL)implementsLayoutMethod;
@end
#pragma mark - ASLayoutElementStyle
AS_EXTERN NSString * const ASLayoutElementStyleWidthProperty;
AS_EXTERN NSString * const ASLayoutElementStyleMinWidthProperty;
AS_EXTERN NSString * const ASLayoutElementStyleMaxWidthProperty;
AS_EXTERN NSString * const ASLayoutElementStyleHeightProperty;
AS_EXTERN NSString * const ASLayoutElementStyleMinHeightProperty;
AS_EXTERN NSString * const ASLayoutElementStyleMaxHeightProperty;
AS_EXTERN NSString * const ASLayoutElementStyleSpacingBeforeProperty;
AS_EXTERN NSString * const ASLayoutElementStyleSpacingAfterProperty;
AS_EXTERN NSString * const ASLayoutElementStyleFlexGrowProperty;
AS_EXTERN NSString * const ASLayoutElementStyleFlexShrinkProperty;
AS_EXTERN NSString * const ASLayoutElementStyleFlexBasisProperty;
AS_EXTERN NSString * const ASLayoutElementStyleAlignSelfProperty;
AS_EXTERN NSString * const ASLayoutElementStyleAscenderProperty;
AS_EXTERN NSString * const ASLayoutElementStyleDescenderProperty;
AS_EXTERN NSString * const ASLayoutElementStyleLayoutPositionProperty;
@protocol ASLayoutElementStyleDelegate <NSObject>
- (void)style:(__kindof ASLayoutElementStyle *)style propertyDidChange:(NSString *)propertyName;
@end
@interface ASLayoutElementStyle : NSObject <ASStackLayoutElement, ASAbsoluteLayoutElement, ASLayoutElementExtensibility, ASLocking>
/**
* @abstract Initializes the layoutElement style with a specified delegate
*/
- (instancetype)initWithDelegate:(id<ASLayoutElementStyleDelegate>)delegate;
/**
* @abstract The object that acts as the delegate of the style.
*
* @discussion The delegate must adopt the ASLayoutElementStyleDelegate protocol. The delegate is not retained.
*/
@property (nullable, nonatomic, weak, readonly) id<ASLayoutElementStyleDelegate> delegate;
#pragma mark - Sizing
/**
* @abstract The width property specifies the width of the content area of an ASLayoutElement.
* The minWidth and maxWidth properties override width.
* Defaults to ASDimensionAuto
*/
@property (nonatomic) ASDimension width;
/**
* @abstract The height property specifies the height of the content area of an ASLayoutElement
* The minHeight and maxHeight properties override height.
* Defaults to ASDimensionAuto
*/
@property (nonatomic) ASDimension height;
/**
* @abstract The minHeight property is used to set the minimum height of a given element. It prevents the used value
* of the height property from becoming smaller than the value specified for minHeight.
* The value of minHeight overrides both maxHeight and height.
* Defaults to ASDimensionAuto
*/
@property (nonatomic) ASDimension minHeight;
/**
* @abstract The maxHeight property is used to set the maximum height of an element. It prevents the used value of the
* height property from becoming larger than the value specified for maxHeight.
* The value of maxHeight overrides height, but minHeight overrides maxHeight.
* Defaults to ASDimensionAuto
*/
@property (nonatomic) ASDimension maxHeight;
/**
* @abstract The minWidth property is used to set the minimum width of a given element. It prevents the used value of
* the width property from becoming smaller than the value specified for minWidth.
* The value of minWidth overrides both maxWidth and width.
* Defaults to ASDimensionAuto
*/
@property (nonatomic) ASDimension minWidth;
/**
* @abstract The maxWidth property is used to set the maximum width of a given element. It prevents the used value of
* the width property from becoming larger than the value specified for maxWidth.
* The value of maxWidth overrides width, but minWidth overrides maxWidth.
* Defaults to ASDimensionAuto
*/
@property (nonatomic) ASDimension maxWidth;
#pragma mark - ASLayoutElementStyleSizeHelpers
/**
* @abstract Provides a suggested size for a layout element. If the optional minSize or maxSize are provided,
* and the preferredSize exceeds these, the minSize or maxSize will be enforced. If this optional value is not
* provided, the layout elements size will default to its intrinsic content size provided calculateSizeThatFits:
*
* @discussion This method is optional, but one of either preferredSize or preferredLayoutSize is required
* for nodes that either have no intrinsic content size or
* should be laid out at a different size than its intrinsic content size. For example, this property could be
* set on an ASImageNode to display at a size different from the underlying image size.
*
* @warning Calling the getter when the size's width or height are relative will cause an assert.
*/
@property (nonatomic) CGSize preferredSize;
/**
* @abstract An optional property that provides a minimum size bound for a layout element. If provided, this restriction will
* always be enforced. If a parent layout elements minimum size is smaller than its childs minimum size, the childs
* minimum size will be enforced and its size will extend out of the layout specs.
*
* @discussion For example, if you set a preferred relative width of 50% and a minimum width of 200 points on an
* element in a full screen container, this would result in a width of 160 points on an iPhone screen. However,
* since 160 pts is lower than the minimum width of 200 pts, the minimum width would be used.
*/
@property (nonatomic) CGSize minSize;
- (CGSize)minSize UNAVAILABLE_ATTRIBUTE;
/**
* @abstract An optional property that provides a maximum size bound for a layout element. If provided, this restriction will
* always be enforced. If a child layout elements maximum size is smaller than its parent, the childs maximum size will
* be enforced and its size will extend out of the layout specs.
*
* @discussion For example, if you set a preferred relative width of 50% and a maximum width of 120 points on an
* element in a full screen container, this would result in a width of 160 points on an iPhone screen. However,
* since 160 pts is higher than the maximum width of 120 pts, the maximum width would be used.
*/
@property (nonatomic) CGSize maxSize;
- (CGSize)maxSize UNAVAILABLE_ATTRIBUTE;
/**
* @abstract Provides a suggested RELATIVE size for a layout element. An ASLayoutSize uses percentages rather
* than points to specify layout. E.g. width should be 50% of the parents width. If the optional minLayoutSize or
* maxLayoutSize are provided, and the preferredLayoutSize exceeds these, the minLayoutSize or maxLayoutSize
* will be enforced. If this optional value is not provided, the layout elements size will default to its intrinsic content size
* provided calculateSizeThatFits:
*/
@property (nonatomic) ASLayoutSize preferredLayoutSize;
/**
* @abstract An optional property that provides a minimum RELATIVE size bound for a layout element. If provided, this
* restriction will always be enforced. If a parent layout elements minimum relative size is smaller than its childs minimum
* relative size, the childs minimum relative size will be enforced and its size will extend out of the layout specs.
*/
@property (nonatomic) ASLayoutSize minLayoutSize;
/**
* @abstract An optional property that provides a maximum RELATIVE size bound for a layout element. If provided, this
* restriction will always be enforced. If a parent layout elements maximum relative size is smaller than its childs maximum
* relative size, the childs maximum relative size will be enforced and its size will extend out of the layout specs.
*/
@property (nonatomic) ASLayoutSize maxLayoutSize;
@end
#pragma mark - ASLayoutElementStylability
@protocol ASLayoutElementStylability
- (instancetype)styledWithBlock:(AS_NOESCAPE void (^)(__kindof ASLayoutElementStyle *style))styleBlock;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,107 @@
//
// ASLayoutElementExtensibility.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
//
#import <UIKit/UIKit.h>
#import <UIKit/UIGeometry.h>
#import <objc/runtime.h>
#pragma mark - ASLayoutElementExtensibility
@protocol ASLayoutElementExtensibility <NSObject>
// The maximum number of extended values per type are defined in ASEnvironment.h above the ASEnvironmentStateExtensions
// struct definition. If you try to set a value at an index after the maximum it will throw an assertion.
- (void)setLayoutOptionExtensionBool:(BOOL)value atIndex:(int)idx;
- (BOOL)layoutOptionExtensionBoolAtIndex:(int)idx;
- (void)setLayoutOptionExtensionInteger:(NSInteger)value atIndex:(int)idx;
- (NSInteger)layoutOptionExtensionIntegerAtIndex:(int)idx;
- (void)setLayoutOptionExtensionEdgeInsets:(UIEdgeInsets)value atIndex:(int)idx;
- (UIEdgeInsets)layoutOptionExtensionEdgeInsetsAtIndex:(int)idx;
@end
#pragma mark - Dynamic Properties
/**
* Unbox NSNumber based on the type
*/
#define ASDK_UNBOX_NUMBER(NUMBER, PROPERTY_TYPE) \
const char *objCType = [NUMBER objCType]; \
if (strcmp(objCType, @encode(BOOL)) == 0) { \
return (PROPERTY_TYPE)[obj boolValue]; \
} else if (strcmp(objCType, @encode(int)) == 0) { \
return (PROPERTY_TYPE)[obj intValue]; \
} else if (strcmp(objCType, @encode(NSInteger)) == 0) { \
return (PROPERTY_TYPE)[obj integerValue]; \
} else if (strcmp(objCType, @encode(NSUInteger)) == 0) { \
return (PROPERTY_TYPE)[obj unsignedIntegerValue]; \
} else if (strcmp(objCType, @encode(CGFloat)) == 0) { \
return (PROPERTY_TYPE)[obj floatValue]; \
} else { \
NSAssert(NO, @"Data type not supported"); \
} \
/**
* Define a NSObject property
*/
#define ASDK_STYLE_PROP_OBJ(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME) \
@dynamic PROPERTY_NAME; \
- (PROPERTY_TYPE)PROPERTY_NAME \
{ \
return (PROPERTY_TYPE)objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \
} \
\
- (void)SETTER_NAME:(PROPERTY_TYPE)PROPERTY_NAME \
{ \
objc_setAssociatedObject(self, @selector(PROPERTY_NAME), PROPERTY_NAME, OBJC_ASSOCIATION_RETAIN); \
} \
/**
* Define an primitive property
*/
#define ASDK_STYLE_PROP_PRIM(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME, DEFAULT_VALUE) \
@dynamic PROPERTY_NAME; \
- (PROPERTY_TYPE)PROPERTY_NAME \
{ \
id obj = objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \
\
if (obj != nil) { \
ASDK_UNBOX_NUMBER(obj, PROPERTY_TYPE); \
} \
\
return DEFAULT_VALUE;\
} \
\
- (void)SETTER_NAME:(PROPERTY_TYPE)PROPERTY_NAME \
{ \
objc_setAssociatedObject(self, @selector(PROPERTY_NAME), @(PROPERTY_NAME), OBJC_ASSOCIATION_RETAIN); \
} \
/**
* Define an structure property
*/
#define ASDK_STYLE_PROP_STR(PROPERTY_TYPE, PROPERTY_NAME, SETTER_NAME, DEFAULT_STRUCT) \
@dynamic PROPERTY_NAME; \
- (PROPERTY_TYPE)PROPERTY_NAME \
{ \
id obj = objc_getAssociatedObject(self, @selector(PROPERTY_NAME)); \
if (obj == nil) { \
return DEFAULT_STRUCT; \
} \
PROPERTY_TYPE PROPERTY_NAME; [obj getValue:&PROPERTY_NAME]; return PROPERTY_NAME; \
} \
\
- (void)SETTER_NAME:(PROPERTY_TYPE)PROPERTY_NAME \
{ \
objc_setAssociatedObject(self, @selector(PROPERTY_NAME), [NSValue value:&PROPERTY_NAME withObjCType:@encode(PROPERTY_TYPE)], OBJC_ASSOCIATION_RETAIN_NONATOMIC);\
} \
@@ -0,0 +1,110 @@
//
// ASLayoutElementPrivate.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <UIKit/UIGeometry.h>
@protocol ASLayoutElement;
@class ASLayoutElementStyle;
#pragma mark - ASLayoutElementContext
NS_ASSUME_NONNULL_BEGIN
AS_SUBCLASSING_RESTRICTED
@interface ASLayoutElementContext : NSObject
@property (nonatomic) int32_t transitionID;
@end
AS_EXTERN int32_t const ASLayoutElementContextInvalidTransitionID;
AS_EXTERN int32_t const ASLayoutElementContextDefaultTransitionID;
// Does not currently support nesting there must be no current context.
AS_EXTERN void ASLayoutElementPushContext(ASLayoutElementContext * context);
AS_EXTERN ASLayoutElementContext * _Nullable ASLayoutElementGetCurrentContext(void);
AS_EXTERN void ASLayoutElementPopContext(void);
NS_ASSUME_NONNULL_END
#pragma mark - ASLayoutElementLayoutDefaults
#define ASLayoutElementLayoutCalculationDefaults \
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize\
{\
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];\
}\
\
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize\
{\
return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];\
}\
\
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize\
restrictedToSize:(ASLayoutElementSize)size\
relativeToParentSize:(CGSize)parentSize\
{\
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutElementSizeResolve(self.style.size, parentSize));\
return [self calculateLayoutThatFits:resolvedRange];\
}\
#pragma mark - ASLayoutElementExtensibility
// Provides extension points for elments that comply to ASLayoutElement like ASLayoutSpec to add additional
// properties besides the default one provided in ASLayoutElementStyle
static const int kMaxLayoutElementBoolExtensions = 1;
static const int kMaxLayoutElementStateIntegerExtensions = 4;
static const int kMaxLayoutElementStateEdgeInsetExtensions = 1;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-folding-constant"
typedef struct ASLayoutElementStyleExtensions {
// Values to store extensions
BOOL boolExtensions[kMaxLayoutElementBoolExtensions];
NSInteger integerExtensions[kMaxLayoutElementStateIntegerExtensions];
UIEdgeInsets edgeInsetsExtensions[kMaxLayoutElementStateEdgeInsetExtensions];
} ASLayoutElementStyleExtensions;
#pragma clang diagnostic pop
#define ASLayoutElementStyleExtensibilityForwarding \
- (void)setLayoutOptionExtensionBool:(BOOL)value atIndex:(int)idx\
{\
[self.style setLayoutOptionExtensionBool:value atIndex:idx];\
}\
\
- (BOOL)layoutOptionExtensionBoolAtIndex:(int)idx\
{\
return [self.style layoutOptionExtensionBoolAtIndex:idx];\
}\
\
- (void)setLayoutOptionExtensionInteger:(NSInteger)value atIndex:(int)idx\
{\
[self.style setLayoutOptionExtensionInteger:value atIndex:idx];\
}\
\
- (NSInteger)layoutOptionExtensionIntegerAtIndex:(int)idx\
{\
return [self.style layoutOptionExtensionIntegerAtIndex:idx];\
}\
\
- (void)setLayoutOptionExtensionEdgeInsets:(UIEdgeInsets)value atIndex:(int)idx\
{\
[self.style setLayoutOptionExtensionEdgeInsets:value atIndex:idx];\
}\
\
- (UIEdgeInsets)layoutOptionExtensionEdgeInsetsAtIndex:(int)idx\
{\
return [self.style layoutOptionExtensionEdgeInsetsAtIndex:idx];\
}\
@@ -0,0 +1,101 @@
//
// ASLayoutSpec.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#import <AsyncDisplayKit/ASAsciiArtBoxCreator.h>
#import <AsyncDisplayKit/ASLocking.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A layout spec is an immutable object that describes a layout, loosely inspired by React.
*/
@interface ASLayoutSpec : NSObject <ASLayoutElement, ASLayoutElementStylability, NSFastEnumeration, ASDescriptionProvider, ASLocking>
/**
* Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a
* layout spec can be created and mutated. Once it is passed back to ASDK, the isMutable flag will be
* set to NO and any further mutations will cause an assert.
*/
@property (nonatomic) BOOL isMutable;
/**
* First child within the children's array.
*
* @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the
* responsibility of holding on to the spec children. Some layout specs, like ASInsetLayoutSpec,
* only require a single child.
*
* For layout specs that require a known number of children (ASBackgroundLayoutSpec, for example)
* a subclass should use this method to set the "primary" child. It can then use setChild:atIndex:
* to set any other required children. Ideally a subclass would hide this from the user, and use the
* setChild:atIndex: internally. For example, ASBackgroundLayoutSpec exposes a "background"
* property that behind the scenes is calling setChild:atIndex:.
*/
@property (nullable, nonatomic) id<ASLayoutElement> child;
/**
* An array of ASLayoutElement children
*
* @discussion Every ASLayoutSpec must act on at least one child. The ASLayoutSpec base class takes the
* reponsibility of holding on to the spec children. Some layout specs, like ASStackLayoutSpec,
* can take an unknown number of children. In this case, the this method should be used.
* For good measure, in these layout specs it probably makes sense to define
* setChild: and setChild:forIdentifier: methods to do something appropriate or to assert.
*/
@property (nullable, nonatomic) NSArray<id<ASLayoutElement>> *children;
@end
/**
* An ASLayoutSpec subclass that can wrap one or more ASLayoutElement and calculates the layout based on the
* sizes of the children. If multiple children are provided the size of the biggest child will be used to for
* size of this layout spec.
*/
@interface ASWrapperLayoutSpec : ASLayoutSpec
/*
* Returns an ASWrapperLayoutSpec object with the given layoutElement as child.
*/
+ (instancetype)wrapperWithLayoutElement:(id<ASLayoutElement>)layoutElement NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
/*
* Returns an ASWrapperLayoutSpec object with the given layoutElements as children.
*/
+ (instancetype)wrapperWithLayoutElements:(NSArray<id<ASLayoutElement>> *)layoutElements NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
/*
* Returns an ASWrapperLayoutSpec object initialized with the given layoutElement as child.
*/
- (instancetype)initWithLayoutElement:(id<ASLayoutElement>)layoutElement AS_WARN_UNUSED_RESULT;
/*
* Returns an ASWrapperLayoutSpec object initialized with the given layoutElements as children.
*/
- (instancetype)initWithLayoutElements:(NSArray<id<ASLayoutElement>> *)layoutElements AS_WARN_UNUSED_RESULT;
/*
* Init not available for ASWrapperLayoutSpec
*/
- (instancetype)init NS_UNAVAILABLE;
@end
@interface ASLayoutSpec (Debugging) <ASDebugNameProvider>
/**
* Used by other layout specs to create ascii art debug strings
*/
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction;
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,159 @@
//
// ASLocking.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <pthread/sched.h>
#import <AsyncDisplayKit/ASAssert.h>
NS_ASSUME_NONNULL_BEGIN
#define kLockSetCapacity 32
/**
* An extension of NSLocking that supports -tryLock.
*/
@protocol ASLocking <NSLocking>
/// Try to take lock without blocking. Returns whether the lock was taken.
- (BOOL)tryLock;
@end
/**
* A set of locks acquired during ASLockSequence.
*/
typedef struct {
unsigned count;
CFTypeRef _Nullable locks[kLockSetCapacity];
} ASLockSet;
/**
* Declare a lock set that is automatically unlocked at the end of scope.
*
* We use this instead of a scope-locking macro because we want to be able
* to step through the lock sequence block in the debugger.
*/
#define ASScopedLockSet __unused ASLockSet __attribute__((cleanup(ASUnlockSet)))
/**
* A block that attempts to add a lock to a lock sequence.
* Such a block is provided to the caller of ASLockSequence.
*
* Returns whether the lock was added. You should return
* NO from your lock sequence body if it returns NO.
*
* For instance, you might write `return addLock(l1) && addLock(l2)`.
*
* @param lock The lock to attempt to add.
* @return YES if the lock was added, NO otherwise.
*/
typedef BOOL(^ASAddLockBlock)(id<ASLocking> lock);
/**
* A block that attempts to lock multiple locks in sequence.
* Such a block is provided by the caller of ASLockSequence.
*
* The block may be run multiple times, if not all locks are immediately
* available. Therefore the block should be idempotent.
*
* The block should attempt to invoke addLock multiple times with
* different locks. It should return NO as soon as any addLock
* operation fails.
*
* For instance, you might write `return addLock(l1) && addLock(l2)`.
*
* @param addLock A block you can call to attempt to add a lock.
* @return YES if all locks were added, NO otherwise.
*/
typedef BOOL(^ASLockSequenceBlock)(NS_NOESCAPE ASAddLockBlock addLock);
/**
* Unlock and release all of the locks in this lock set.
*/
NS_INLINE void ASUnlockSet(ASLockSet *lockSet) {
for (unsigned i = 0; i < lockSet->count; i++) {
CFTypeRef lock = lockSet->locks[i];
[(__bridge id<ASLocking>)lock unlock];
CFRelease(lock);
}
}
/**
* Take multiple locks "simultaneously," avoiding deadlocks
* caused by lock ordering.
*
* The block you provide should attempt to take a series of locks,
* using the provided `addLock` block. As soon as any addLock fails,
* you should return NO.
*
* For example:
* ASLockSequence(^(ASAddLockBlock addLock) ^{
* return addLock(l0) && addLock(l1);
* });
*
* Note: This function doesn't protect from lock ordering deadlocks if
* one of the locks is already locked (recursive.) Only locks taken
* inside this function are guaranteed not to cause a deadlock.
*/
NS_INLINE ASLockSet ASLockSequence(NS_NOESCAPE ASLockSequenceBlock body)
{
__block ASLockSet locks = (ASLockSet){0, {}};
BOOL (^addLock)(id<ASLocking>) = ^(id<ASLocking> obj) {
// nil lock = ignore.
if (!obj) {
return YES;
}
// If they go over capacity, assert and return YES.
// If we return NO, they will enter an infinite loop.
if (locks.count == kLockSetCapacity) {
ASDisplayNodeCFailAssert(@"Locking more than %d locks at once is not supported.", kLockSetCapacity);
return YES;
}
if ([obj tryLock]) {
locks.locks[locks.count++] = (__bridge_retained CFTypeRef)obj;
return YES;
}
return NO;
};
/**
* Repeatedly try running their block, passing in our `addLock`
* until it succeeds. If it fails, unlock all and yield the thread
* to reduce spinning.
*/
while (true) {
if (body(addLock)) {
// Success
return locks;
} else {
ASUnlockSet(&locks);
locks.count = 0;
sched_yield();
}
}
}
/**
* These Foundation classes already implement -tryLock.
*/
@interface NSLock (ASLocking) <ASLocking>
@end
@interface NSRecursiveLock (ASLocking) <ASLocking>
@end
@interface NSConditionLock (ASLocking) <ASLocking>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,57 @@
//
// ASMainThreadDeallocation.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (ASMainThreadIvarTeardown)
/**
* Call this from -dealloc to schedule this instance's
* ivars for main thread deallocation as needed.
*
* This method includes a check for whether it's on the main thread,
* and it will do nothing in that case.
*/
- (void)scheduleIvarsForMainThreadDeallocation;
@end
@interface NSObject (ASNeedsMainThreadDeallocation)
/**
* Override this property to indicate that instances of this
* class need to be deallocated on the main thread.
* You do not access this property yourself.
*
* The NSObject implementation returns YES if the class name has
* a prefix UI, AV, or CA. This property is also overridden to
* return fixed values for other common classes, such as UIImage,
* UIGestureRecognizer, and UIResponder.
*/
@property (class, readonly) BOOL needsMainThreadDeallocation;
@end
@interface NSProxy (ASNeedsMainThreadDeallocation)
/**
* Override this property to indicate that instances of this
* class need to be deallocated on the main thread.
* You do not access this property yourself.
*
* The NSProxy implementation returns NO because
* proxies almost always hold weak references.
*/
@property (class, readonly) BOOL needsMainThreadDeallocation;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,66 @@
//
// ASObjectDescriptionHelpers.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASDebugNameProvider <NSObject>
@required
/**
* @abstract Name that is printed by ascii art string and displayed in description.
*/
@property (nullable, nonatomic, copy) NSString *debugName;
@end
/**
* Your base class should conform to this and override `-debugDescription`
* to call `[self propertiesForDebugDescription]` and use `ASObjectDescriptionMake`
* to return a string. Subclasses of this base class just need to override
* `propertiesForDebugDescription`, call super, and modify the result as needed.
*/
@protocol ASDebugDescriptionProvider
@required
- (NSMutableArray<NSDictionary *> *)propertiesForDebugDescription;
@end
/**
* Your base class should conform to this and override `-description`
* to call `[self propertiesForDescription]` and use `ASObjectDescriptionMake`
* to return a string. Subclasses of this base class just need to override
* `propertiesForDescription`, call super, and modify the result as needed.
*/
@protocol ASDescriptionProvider
@required
- (NSMutableArray<NSDictionary *> *)propertiesForDescription;
@end
AS_EXTERN NSString *ASGetDescriptionValueString(id object);
/// Useful for structs etc. Returns e.g. { position = (0 0); frame = (0 0; 50 50) }
AS_EXTERN NSString *ASObjectDescriptionMakeWithoutObject(NSArray<NSDictionary *> * _Nullable propertyGroups);
/// Returns e.g. <MYObject: 0xFFFFFFFF; name = "Object Name"; frame = (0 0; 50 50)>
AS_EXTERN NSString *ASObjectDescriptionMake(__autoreleasing id object, NSArray<NSDictionary *> * _Nullable propertyGroups);
/**
* Returns e.g. <MYObject: 0xFFFFFFFF>
*
* Note: `object` param is autoreleasing so that this function is dealloc-safe.
* No, unsafe_unretained isn't acceptable here the optimizer may deallocate object early.
*/
AS_EXTERN NSString *ASObjectDescriptionMakeTiny(__autoreleasing id _Nullable object);
AS_EXTERN NSString * _Nullable ASStringWithQuotesIfMultiword(NSString * _Nullable string);
NS_ASSUME_NONNULL_END
@@ -0,0 +1,97 @@
//
// ASRecursiveUnfairLock.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <pthread/pthread.h>
#if defined(__aarch64__)
#define AS_USE_OS_LOCK true
#else
#define AS_USE_OS_LOCK false
#endif
#if AS_USE_OS_LOCK
#import <os/lock.h>
// Note: We don't use ATOMIC_VAR_INIT here because C++ compilers don't like it,
// and it literally does absolutely nothing.
#define AS_RECURSIVE_UNFAIR_LOCK_INIT ((ASRecursiveUnfairLock){ OS_UNFAIR_LOCK_INIT, NULL, 0})
NS_ASSUME_NONNULL_BEGIN
OS_UNFAIR_LOCK_AVAILABILITY
typedef struct {
os_unfair_lock _lock OS_UNFAIR_LOCK_AVAILABILITY;
_Atomic(pthread_t) _thread; // Write-protected by lock
int _count; // Protected by lock
} ASRecursiveUnfairLock;
/**
* Lock, blocking if needed.
*/
AS_EXTERN OS_UNFAIR_LOCK_AVAILABILITY
void ASRecursiveUnfairLockLock(ASRecursiveUnfairLock *l);
/**
* Try to lock without blocking. Returns whether we took the lock.
*/
AS_EXTERN OS_UNFAIR_LOCK_AVAILABILITY
BOOL ASRecursiveUnfairLockTryLock(ASRecursiveUnfairLock *l);
/**
* Unlock. Calling this on a thread that does not own
* the lock will result in an assertion failure, and undefined
* behavior if foundation assertions are disabled.
*/
AS_EXTERN OS_UNFAIR_LOCK_AVAILABILITY
void ASRecursiveUnfairLockUnlock(ASRecursiveUnfairLock *l);
NS_ASSUME_NONNULL_END
#else
#import <libkern/OSAtomic.h>
// Note: We don't use ATOMIC_VAR_INIT here because C++ compilers don't like it,
// and it literally does absolutely nothing.
#define AS_RECURSIVE_UNFAIR_LOCK_INIT ((ASRecursiveUnfairLock){ OS_SPINLOCK_INIT, NULL, 0})
NS_ASSUME_NONNULL_BEGIN
typedef struct {
OSSpinLock _lock;
_Atomic(pthread_t) _thread; // Write-protected by lock
int _count; // Protected by lock
} ASRecursiveUnfairLock;
/**
* Lock, blocking if needed.
*/
AS_EXTERN
void ASRecursiveUnfairLockLock(ASRecursiveUnfairLock *l);
/**
* Try to lock without blocking. Returns whether we took the lock.
*/
AS_EXTERN
BOOL ASRecursiveUnfairLockTryLock(ASRecursiveUnfairLock *l);
/**
* Unlock. Calling this on a thread that does not own
* the lock will result in an assertion failure, and undefined
* behavior if foundation assertions are disabled.
*/
AS_EXTERN
void ASRecursiveUnfairLockUnlock(ASRecursiveUnfairLock *l);
NS_ASSUME_NONNULL_END
#endif
@@ -0,0 +1,93 @@
//
// ASRunLoopQueue.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASLocking.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASCATransactionQueueObserving <NSObject>
- (void)prepareForCATransactionCommit;
@end
@interface ASAbstractRunLoopQueue : NSObject
@end
AS_SUBCLASSING_RESTRICTED
@interface ASRunLoopQueue<ObjectType> : ASAbstractRunLoopQueue <ASLocking>
/**
* Create a new queue with the given run loop and handler.
*
* @param runloop The run loop that will drive this queue.
* @param retainsObjects Whether the queue should retain its objects.
* @param handlerBlock An optional block to be run for each enqueued object.
*
* @discussion You may pass @c nil for the handler if you simply want the objects to
* be retained at enqueue time, and released during the run loop step. This is useful
* for creating a "main deallocation queue", as @c ASDeallocQueue creates its own
* worker thread with its own run loop.
*/
- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop
retainObjects:(BOOL)retainsObjects
handler:(nullable void(^)(ObjectType dequeuedItem, BOOL isQueueDrained))handlerBlock;
- (void)enqueue:(ObjectType)object;
@property (readonly) BOOL isEmpty;
@property (nonatomic) NSUInteger batchSize; // Default == 1.
@property (nonatomic) BOOL ensureExclusiveMembership; // Default == YES. Set-like behavior.
@end
/**
* The queue to run on main run loop before CATransaction commit.
*
* @discussion this queue will run after ASRunLoopQueue and before CATransaction commit
* to get last chance of updating/coalesce info like interface state.
* Each node will only be called once per transaction commit to reflect interface change.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASCATransactionQueue : ASAbstractRunLoopQueue
@property (readonly) BOOL isEmpty;
@property (readonly, getter=isEnabled) BOOL enabled;
- (void)enqueue:(id<ASCATransactionQueueObserving>)object;
@end
extern ASCATransactionQueue *_ASSharedCATransactionQueue;
extern dispatch_once_t _ASSharedCATransactionQueueOnceToken;
NS_INLINE ASCATransactionQueue *ASCATransactionQueueGet(void) {
dispatch_once(&_ASSharedCATransactionQueueOnceToken, ^{
_ASSharedCATransactionQueue = [[ASCATransactionQueue alloc] init];
});
return _ASSharedCATransactionQueue;
}
@interface ASDeallocQueue : NSObject
@property (class, readonly) ASDeallocQueue *sharedDeallocationQueue;
+ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED;
- (void)drain;
- (void)releaseObjectInBackground:(id __strong _Nullable * _Nonnull)objectPtr;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,38 @@
//
// ASScrollDirection.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreGraphics/CGAffineTransform.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_OPTIONS(NSInteger, ASScrollDirection) {
ASScrollDirectionNone = 0,
ASScrollDirectionRight = 1 << 0,
ASScrollDirectionLeft = 1 << 1,
ASScrollDirectionUp = 1 << 2,
ASScrollDirectionDown = 1 << 3
};
AS_EXTERN const ASScrollDirection ASScrollDirectionHorizontalDirections;
AS_EXTERN const ASScrollDirection ASScrollDirectionVerticalDirections;
AS_EXTERN BOOL ASScrollDirectionContainsVerticalDirection(ASScrollDirection scrollDirection);
AS_EXTERN BOOL ASScrollDirectionContainsHorizontalDirection(ASScrollDirection scrollDirection);
AS_EXTERN BOOL ASScrollDirectionContainsRight(ASScrollDirection scrollDirection);
AS_EXTERN BOOL ASScrollDirectionContainsLeft(ASScrollDirection scrollDirection);
AS_EXTERN BOOL ASScrollDirectionContainsUp(ASScrollDirection scrollDirection);
AS_EXTERN BOOL ASScrollDirectionContainsDown(ASScrollDirection scrollDirection);
AS_EXTERN ASScrollDirection ASScrollDirectionApplyTransform(ASScrollDirection scrollDirection, CGAffineTransform transform);
NS_ASSUME_NONNULL_END
@@ -0,0 +1,53 @@
//
// ASScrollNode.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASScrollDirection.h>
NS_ASSUME_NONNULL_BEGIN
@class UIScrollView;
/**
* Simple node that wraps UIScrollView.
*/
@interface ASScrollNode : ASDisplayNode
/**
* @abstract The node's UIScrollView.
*/
@property (readonly) UIScrollView *view;
/**
* @abstract When enabled, the size calculated by the node's layout spec is used as
* the .contentSize of the scroll view, instead of the bounds size. The bounds is instead
* allowed to match the parent's size (whenever it is finite - otherwise, the bounds size
* also grows to the full contentSize). It also works with .layoutSpecBlock().
* NOTE: Most users of ASScrollNode will want to use this, and may be enabled by default later.
*/
@property BOOL automaticallyManagesContentSize;
/**
* @abstract This property controls how the constrainedSize is interpreted when sizing the content.
* if you are using automaticallyManagesContentSize, it can be crucial to ensure that the sizing is
* done how you expect.
* Vertical: The constrainedSize is interpreted as having unbounded .height (CGFLOAT_MAX), allowing
* stacks and other content in the layout spec to expand and result in scrollable content.
* Horizontal: The constrainedSize is interpreted as having unbounded .width (CGFLOAT_MAX), ...
* Vertical & Horizontal: the constrainedSize is interpreted as unbounded in both directions.
* @default ASScrollDirectionVerticalDirections
*/
@property ASScrollDirection scrollableDirections;
@property BOOL canCancelAllTouchesInViews;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,143 @@
//
// ASStackLayoutDefines.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
/** The direction children are stacked in */
typedef NS_ENUM(NSUInteger, ASStackLayoutDirection) {
/** Children are stacked vertically */
ASStackLayoutDirectionVertical,
/** Children are stacked horizontally */
ASStackLayoutDirectionHorizontal,
};
/** If no children are flexible, how should this spec justify its children in the available space? */
typedef NS_ENUM(NSUInteger, ASStackLayoutJustifyContent) {
/**
On overflow, children overflow out of this spec's bounds on the right/bottom side.
On underflow, children are left/top-aligned within this spec's bounds.
*/
ASStackLayoutJustifyContentStart,
/**
On overflow, children are centered and overflow on both sides.
On underflow, children are centered within this spec's bounds in the stacking direction.
*/
ASStackLayoutJustifyContentCenter,
/**
On overflow, children overflow out of this spec's bounds on the left/top side.
On underflow, children are right/bottom-aligned within this spec's bounds.
*/
ASStackLayoutJustifyContentEnd,
/**
On overflow or if the stack has only 1 child, this value is identical to ASStackLayoutJustifyContentStart.
Otherwise, the starting edge of the first child is at the starting edge of the stack,
the ending edge of the last child is at the ending edge of the stack, and the remaining children
are distributed so that the spacing between any two adjacent ones is the same.
If there is a remaining space after spacing division, it is combined with the last spacing (i.e the one between the last 2 children).
*/
ASStackLayoutJustifyContentSpaceBetween,
/**
On overflow or if the stack has only 1 child, this value is identical to ASStackLayoutJustifyContentCenter.
Otherwise, children are distributed such that the spacing between any two adjacent ones is the same,
and the spacing between the first/last child and the stack edges is half the size of the spacing between children.
If there is a remaining space after spacing division, it is combined with the last spacing (i.e the one between the last child and the stack ending edge).
*/
ASStackLayoutJustifyContentSpaceAround
};
/** Orientation of children along cross axis */
typedef NS_ENUM(NSUInteger, ASStackLayoutAlignItems) {
/** Align children to start of cross axis */
ASStackLayoutAlignItemsStart,
/** Align children with end of cross axis */
ASStackLayoutAlignItemsEnd,
/** Center children on cross axis */
ASStackLayoutAlignItemsCenter,
/** Expand children to fill cross axis */
ASStackLayoutAlignItemsStretch,
/** Children align to their first baseline. Only available for horizontal stack spec */
ASStackLayoutAlignItemsBaselineFirst,
/** Children align to their last baseline. Only available for horizontal stack spec */
ASStackLayoutAlignItemsBaselineLast,
ASStackLayoutAlignItemsNotSet
};
/**
Each child may override their parent stack's cross axis alignment.
@see ASStackLayoutAlignItems
*/
typedef NS_ENUM(NSUInteger, ASStackLayoutAlignSelf) {
/** Inherit alignment value from containing stack. */
ASStackLayoutAlignSelfAuto,
/** Align to start of cross axis */
ASStackLayoutAlignSelfStart,
/** Align with end of cross axis */
ASStackLayoutAlignSelfEnd,
/** Center on cross axis */
ASStackLayoutAlignSelfCenter,
/** Expand to fill cross axis */
ASStackLayoutAlignSelfStretch,
};
/** Whether children are stacked into a single or multiple lines. */
typedef NS_ENUM(NSUInteger, ASStackLayoutFlexWrap) {
ASStackLayoutFlexWrapNoWrap,
ASStackLayoutFlexWrapWrap,
};
/** Orientation of lines along cross axis if there are multiple lines. */
typedef NS_ENUM(NSUInteger, ASStackLayoutAlignContent) {
ASStackLayoutAlignContentStart,
ASStackLayoutAlignContentCenter,
ASStackLayoutAlignContentEnd,
ASStackLayoutAlignContentSpaceBetween,
ASStackLayoutAlignContentSpaceAround,
ASStackLayoutAlignContentStretch,
};
/** Orientation of children along horizontal axis */
typedef NS_ENUM(NSUInteger, ASHorizontalAlignment) {
/** No alignment specified. Default value */
ASHorizontalAlignmentNone,
/** Left aligned */
ASHorizontalAlignmentLeft,
/** Center aligned */
ASHorizontalAlignmentMiddle,
/** Right aligned */
ASHorizontalAlignmentRight,
// After 2.0 has landed, we'll add ASDISPLAYNODE_DEPRECATED here - for now, avoid triggering errors for projects with -Werror
/** @deprecated Use ASHorizontalAlignmentLeft instead */
ASAlignmentLeft ASDISPLAYNODE_DEPRECATED = ASHorizontalAlignmentLeft,
/** @deprecated Use ASHorizontalAlignmentMiddle instead */
ASAlignmentMiddle ASDISPLAYNODE_DEPRECATED = ASHorizontalAlignmentMiddle,
/** @deprecated Use ASHorizontalAlignmentRight instead */
ASAlignmentRight ASDISPLAYNODE_DEPRECATED = ASHorizontalAlignmentRight,
};
/** Orientation of children along vertical axis */
typedef NS_ENUM(NSUInteger, ASVerticalAlignment) {
/** No alignment specified. Default value */
ASVerticalAlignmentNone,
/** Top aligned */
ASVerticalAlignmentTop,
/** Center aligned */
ASVerticalAlignmentCenter,
/** Bottom aligned */
ASVerticalAlignmentBottom,
// After 2.0 has landed, we'll add ASDISPLAYNODE_DEPRECATED here - for now, avoid triggering errors for projects with -Werror
/** @deprecated Use ASVerticalAlignmentTop instead */
ASAlignmentTop ASDISPLAYNODE_DEPRECATED = ASVerticalAlignmentTop,
/** @deprecated Use ASVerticalAlignmentCenter instead */
ASAlignmentCenter ASDISPLAYNODE_DEPRECATED = ASVerticalAlignmentCenter,
/** @deprecated Use ASVerticalAlignmentBottom instead */
ASAlignmentBottom ASDISPLAYNODE_DEPRECATED = ASVerticalAlignmentBottom,
};
@@ -0,0 +1,74 @@
//
// ASStackLayoutElement.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
NS_ASSUME_NONNULL_BEGIN
/**
* Layout options that can be defined for an ASLayoutElement being added to a ASStackLayoutSpec.
*/
@protocol ASStackLayoutElement <NSObject>
/**
* @abstract Additional space to place before this object in the stacking direction.
* Used when attached to a stack layout.
*/
@property (nonatomic) CGFloat spacingBefore;
/**
* @abstract Additional space to place after this object in the stacking direction.
* Used when attached to a stack layout.
*/
@property (nonatomic) CGFloat spacingAfter;
/**
* @abstract If the sum of childrens' stack dimensions is less than the minimum size, how much should this component grow?
* This value represents the "flex grow factor" and determines how much this component should grow in relation to any
* other flexible children.
*/
@property (nonatomic) CGFloat flexGrow;
/**
* @abstract If the sum of childrens' stack dimensions is greater than the maximum size, how much should this component shrink?
* This value represents the "flex shrink factor" and determines how much this component should shink in relation to
* other flexible children.
*/
@property (nonatomic) CGFloat flexShrink;
/**
* @abstract Specifies the initial size in the stack dimension for this object.
* Defaults to ASDimensionAuto.
* Used when attached to a stack layout.
*/
@property (nonatomic) ASDimension flexBasis;
/**
* @abstract Orientation of the object along cross axis, overriding alignItems.
* Defaults to ASStackLayoutAlignSelfAuto.
* Used when attached to a stack layout.
*/
@property (nonatomic) ASStackLayoutAlignSelf alignSelf;
/**
* @abstract Used for baseline alignment. The distance from the top of the object to its baseline.
*/
@property (nonatomic) CGFloat ascender;
/**
* @abstract Used for baseline alignment. The distance from the baseline of the object to its bottom.
*/
@property (nonatomic) CGFloat descender;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,64 @@
//
// ASTextKitComponents.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@interface ASTextKitComponentsTextView : UITextView
- (instancetype)initWithFrame:(CGRect)frame textContainer:(nullable NSTextContainer *)textContainer NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder __unavailable;
- (instancetype)init __unavailable;
@end
AS_SUBCLASSING_RESTRICTED
@interface ASTextKitComponents : NSObject
/**
@abstract Creates the stack of TextKit components.
@param attributedSeedString The attributed string to seed the returned text storage with, or nil to receive an blank text storage.
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.
*/
+ (instancetype)componentsWithAttributedSeedString:(nullable NSAttributedString *)attributedSeedString
textContainerSize:(CGSize)textContainerSize NS_RETURNS_RETAINED;
/**
@abstract Creates the stack of TextKit components.
@param textStorage The NSTextStorage to use.
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
@param layoutManager The NSLayoutManager to use.
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.
*/
+ (instancetype)componentsWithTextStorage:(NSTextStorage *)textStorage
textContainerSize:(CGSize)textContainerSize
layoutManager:(NSLayoutManager *)layoutManager NS_RETURNS_RETAINED;
/**
@abstract Returns the bounding size for the text view's text.
@param constrainedWidth The constraining width to be used during text-sizing. Usually, this value should be the receiver's calculated size.
@result A CGSize representing the bounding size for the receiver's text.
*/
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth;
- (CGSize)sizeForConstrainedWidth:(CGFloat)constrainedWidth
forMaxNumberOfLines:(NSInteger)numberOfLines;
@property (nonatomic, readonly) NSTextStorage *textStorage;
@property (nonatomic, readonly) NSTextContainer *textContainer;
@property (nonatomic, readonly) NSLayoutManager *layoutManager;
@property (nonatomic, nullable) ASTextKitComponentsTextView *textView;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,13 @@
//
// ASTextNodeTypes.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
//
#pragma once
// Use this attribute name to add "word kerning"
static NSString *const ASTextNodeWordKerningAttributeName = @"ASAttributedStringWordKerning";
@@ -0,0 +1,315 @@
//
// ASThread.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
//
#import <Foundation/Foundation.h>
#import <pthread.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASConfigurationInternal.h>
#import <AsyncDisplayKit/ASRecursiveUnfairLock.h>
ASDISPLAYNODE_INLINE AS_WARN_UNUSED_RESULT BOOL ASDisplayNodeThreadIsMain()
{
return 0 != pthread_main_np();
}
/**
* Adds the lock to the current scope.
*
* A C version of the C++ lockers. Pass in any id<NSLocking>.
* One benefit this has over C++ lockers is that the lock is retained. We
* had bugs in the past where an object would be deallocated while someone
* had locked its instanceLock, and we'd get a crash. This macro
* retains the locked object until it can be unlocked, which is nice.
*/
#define ASLockScope(nsLocking) \
id<NSLocking> __lockToken __attribute__((cleanup(_ASLockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \
[__lockToken lock];
/// Same as ASLockScope(1) but lock isn't retained (be careful).
#define ASLockScopeUnowned(nsLocking) \
__unsafe_unretained id<NSLocking> __lockToken __attribute__((cleanup(_ASLockScopeUnownedCleanup))) = nsLocking; \
[__lockToken lock];
ASDISPLAYNODE_INLINE void _ASLockScopeCleanup(id<NSLocking> __strong * const lockPtr) {
[*lockPtr unlock];
}
ASDISPLAYNODE_INLINE void _ASLockScopeUnownedCleanup(id<NSLocking> __unsafe_unretained * const lockPtr) {
[*lockPtr unlock];
}
/**
* Same as ASLockScope(1) but it uses self, so we can skip retain/release.
*/
#define ASLockScopeSelf() ASLockScopeUnowned(self)
/// One-liner while holding the lock.
#define ASLocked(nsLocking, expr) ({ ASLockScope(nsLocking); expr; })
/// Faster self-version.
#define ASLockedSelf(expr) ({ ASLockScopeSelf(); expr; })
#define ASLockedSelfCompareAssign(lvalue, newValue) \
ASLockedSelf(ASCompareAssign(lvalue, newValue))
#define ASLockedSelfCompareAssignObjects(lvalue, newValue) \
ASLockedSelf(ASCompareAssignObjects(lvalue, newValue))
#define ASLockedSelfCompareAssignCustom(lvalue, newValue, isequal) \
ASLockedSelf(ASCompareAssignCustom(lvalue, newValue, isequal))
#define ASLockedSelfCompareAssignCopy(lvalue, obj) \
ASLockedSelf(ASCompareAssignCopy(lvalue, obj))
#define ASUnlockScope(nsLocking) \
id<NSLocking> __lockToken __attribute__((cleanup(_ASUnlockScopeCleanup))) NS_VALID_UNTIL_END_OF_SCOPE = nsLocking; \
[__lockToken unlock];
#define ASSynthesizeLockingMethodsWithMutex(mutex) \
- (void)lock { mutex.lock(); } \
- (void)unlock { mutex.unlock(); } \
- (BOOL)tryLock { return (BOOL)mutex.try_lock(); }
#define ASSynthesizeLockingMethodsWithObject(object) \
- (void)lock { [object lock]; } \
- (void)unlock { [object unlock]; } \
- (BOOL)tryLock { return [object tryLock]; }
ASDISPLAYNODE_INLINE void _ASUnlockScopeCleanup(id<NSLocking> __strong *lockPtr) {
[*lockPtr lock];
}
#ifdef __cplusplus
#include <memory>
#include <mutex>
#include <new>
#include <thread>
// These macros are here for legacy reasons. We may get rid of them later.
#define ASAssertLocked(m) m.AssertHeld()
#define ASAssertUnlocked(m) m.AssertNotHeld()
namespace AS {
// Set once in Mutex constructor. Linker fails if this is a member variable. ??
static bool gMutex_unfair;
// Silence unguarded availability warnings in here, because
// perf is critical and we will check availability once
// and not again.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
class Mutex
{
public:
/// Constructs a plain mutex (the default).
Mutex () : Mutex (false) {}
~Mutex () {
// Manually destroy since unions can't do it.
switch (_type) {
case Plain:
_plain.~mutex();
break;
case Recursive:
_recursive.~recursive_mutex();
break;
case Unfair:
// nop
break;
case RecursiveUnfair:
// nop
break;
}
}
Mutex (const Mutex&) = delete;
Mutex &operator=(const Mutex&) = delete;
bool try_lock() {
bool success = false;
switch (_type) {
case Plain:
success = _plain.try_lock();
break;
case Recursive:
success = _recursive.try_lock();
break;
case Unfair:
#if AS_USE_OS_LOCK
success = os_unfair_lock_trylock(&_unfair);
#else
success = OSSpinLockTry(&_unfair);
#endif
break;
case RecursiveUnfair:
success = ASRecursiveUnfairLockTryLock(&_runfair);
break;
}
if (success) {
DidLock();
}
return success;
}
void lock() {
switch (_type) {
case Plain:
_plain.lock();
break;
case Recursive:
_recursive.lock();
break;
case Unfair:
#if AS_USE_OS_LOCK
os_unfair_lock_lock(&_unfair);
#else
OSSpinLockLock(&_unfair);
#endif
break;
case RecursiveUnfair:
ASRecursiveUnfairLockLock(&_runfair);
break;
}
DidLock();
}
void unlock() {
WillUnlock();
switch (_type) {
case Plain:
_plain.unlock();
break;
case Recursive:
_recursive.unlock();
break;
case Unfair:
#if AS_USE_OS_LOCK
os_unfair_lock_unlock(&_unfair);
#else
OSSpinLockUnlock(&_unfair);
#endif
break;
case RecursiveUnfair:
ASRecursiveUnfairLockUnlock(&_runfair);
break;
}
}
void AssertHeld() {
ASDisplayNodeCAssert(_owner == std::this_thread::get_id(), @"Thread should hold lock");
}
void AssertNotHeld() {
ASDisplayNodeCAssert(_owner != std::this_thread::get_id(), @"Thread should not hold lock");
}
explicit Mutex (bool recursive) {
// Check if we can use unfair lock and store in static var.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (AS_AVAILABLE_IOS_TVOS(10, 10)) {
gMutex_unfair = ASActivateExperimentalFeature(ASExperimentalUnfairLock);
}
gMutex_unfair = true;
});
if (recursive) {
if (gMutex_unfair) {
_type = RecursiveUnfair;
_runfair = AS_RECURSIVE_UNFAIR_LOCK_INIT;
} else {
_type = Recursive;
new (&_recursive) std::recursive_mutex();
}
} else {
if (gMutex_unfair) {
_type = Unfair;
#if AS_USE_OS_LOCK
_unfair = OS_UNFAIR_LOCK_INIT;
#else
_unfair = OS_SPINLOCK_INIT;
#endif
} else {
_type = Plain;
new (&_plain) std::mutex();
}
}
}
private:
enum Type {
Plain,
Recursive,
Unfair,
RecursiveUnfair
};
void WillUnlock() {
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
if (--_count == 0) {
_owner = std::thread::id();
}
#endif
}
void DidLock() {
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
if (++_count == 1) {
// New owner.
_owner = std::this_thread::get_id();
}
#endif
}
Type _type;
union {
#if AS_USE_OS_LOCK
os_unfair_lock _unfair;
#else
OSSpinLock _unfair;
#endif
ASRecursiveUnfairLock _runfair;
std::mutex _plain;
std::recursive_mutex _recursive;
};
#if ASDISPLAYNODE_ASSERTIONS_ENABLED
std::thread::id _owner = std::thread::id();
int _count = 0;
#endif
};
#pragma clang diagnostic pop // ignored "-Wunguarded-availability"
/**
Obj-C doesn't allow you to pass parameters to C++ ivar constructors.
Provide a convenience to change the default from non-recursive to recursive.
But wait! Recursive mutexes are a bad idea. Think twice before using one:
http://www.zaval.org/resources/library/butenhof1.html
http://www.fieryrobot.com/blog/2008/10/14/recursive-locks-will-kill-you/
*/
class RecursiveMutex : public Mutex
{
public:
RecursiveMutex () : Mutex (true) {}
};
typedef std::lock_guard<Mutex> MutexLocker;
typedef std::unique_lock<Mutex> UniqueLock;
} // namespace AS
#endif /* __cplusplus */
@@ -0,0 +1,174 @@
//
// ASTraitCollection.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
@class ASTraitCollection;
@protocol ASLayoutElement;
@protocol ASTraitEnvironment;
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASPrimitiveTraitCollection
/**
* @abstract This is an internal struct-representation of ASTraitCollection.
*
* @discussion This struct is for internal use only. Framework users should always use ASTraitCollection.
*
* If you use ASPrimitiveTraitCollection, please do make sure to initialize it with ASPrimitiveTraitCollectionMakeDefault()
* or ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection*).
*/
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
typedef struct {
UIUserInterfaceSizeClass horizontalSizeClass;
UIUserInterfaceSizeClass verticalSizeClass;
CGFloat displayScale;
UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0));
UIUserInterfaceIdiom userInterfaceIdiom;
UIForceTouchCapability forceTouchCapability;
UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0));
#if AS_BUILD_UIUSERINTERFACESTYLE
UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0));
#endif
// NOTE: This must be a constant. We will assert.
unowned UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0));
CGSize containerSize;
} ASPrimitiveTraitCollection;
#pragma clang diagnostic pop
/**
* Creates ASPrimitiveTraitCollection with default values.
*/
AS_EXTERN ASPrimitiveTraitCollection ASPrimitiveTraitCollectionMakeDefault(void);
/**
* Creates a ASPrimitiveTraitCollection from a given UITraitCollection.
*/
AS_EXTERN ASPrimitiveTraitCollection ASPrimitiveTraitCollectionFromUITraitCollection(UITraitCollection *traitCollection);
/**
* Compares two ASPrimitiveTraitCollection to determine if they are the same.
*/
AS_EXTERN BOOL ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(ASPrimitiveTraitCollection lhs, ASPrimitiveTraitCollection rhs);
/**
* Returns a string representation of a ASPrimitiveTraitCollection.
*/
AS_EXTERN NSString *NSStringFromASPrimitiveTraitCollection(ASPrimitiveTraitCollection traits);
/**
* This function will walk the layout element hierarchy and updates the layout element trait collection for every
* layout element within the hierarchy.
*/
AS_EXTERN void ASTraitCollectionPropagateDown(id<ASLayoutElement> element, ASPrimitiveTraitCollection traitCollection);
/**
* Abstraction on top of UITraitCollection for propagation within AsyncDisplayKit-Layout
*/
@protocol ASTraitEnvironment <NSObject>
/**
* @abstract Returns a struct-representation of the environment's ASEnvironmentDisplayTraits.
*
* @discussion This only exists as an internal convenience method. Users should access the trait collections through
* the NSObject based asyncTraitCollection API
*/
- (ASPrimitiveTraitCollection)primitiveTraitCollection;
/**
* @abstract Sets a trait collection on this environment state.
*
* @discussion This only exists as an internal convenience method. Users should not override trait collection using it.
* Use [ASViewController overrideDisplayTraitsWithTraitCollection] block instead.
*/
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection;
/**
* @abstract Returns the thread-safe UITraitCollection equivalent.
*/
- (ASTraitCollection *)asyncTraitCollection;
@end
#define ASPrimitiveTraitCollectionDefaults \
- (ASPrimitiveTraitCollection)primitiveTraitCollection\
{\
return _primitiveTraitCollection.load();\
}\
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection\
{\
_primitiveTraitCollection = traitCollection;\
}\
#define ASLayoutElementCollectionTableSetTraitCollection(lock) \
- (void)setPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traitCollection\
{\
AS::MutexLocker l(lock);\
\
ASPrimitiveTraitCollection oldTraits = self.primitiveTraitCollection;\
[super setPrimitiveTraitCollection:traitCollection];\
\
/* Extra Trait Collection Handling */\
\
/* If the node is not loaded yet don't do anything as otherwise the access of the view will trigger a load */\
if (! self.isNodeLoaded) { return; }\
\
ASPrimitiveTraitCollection currentTraits = self.primitiveTraitCollection;\
if (ASPrimitiveTraitCollectionIsEqualToASPrimitiveTraitCollection(currentTraits, oldTraits) == NO) {\
[self.dataController environmentDidChange];\
}\
}\
#pragma mark - ASTraitCollection
AS_SUBCLASSING_RESTRICTED
@interface ASTraitCollection : NSObject
@property (readonly) UIUserInterfaceSizeClass horizontalSizeClass;
@property (readonly) UIUserInterfaceSizeClass verticalSizeClass;
@property (readonly) CGFloat displayScale;
@property (readonly) UIDisplayGamut displayGamut API_AVAILABLE(ios(10.0));
@property (readonly) UIUserInterfaceIdiom userInterfaceIdiom;
@property (readonly) UIForceTouchCapability forceTouchCapability;
@property (readonly) UITraitEnvironmentLayoutDirection layoutDirection API_AVAILABLE(ios(10.0));
#if AS_BUILD_UIUSERINTERFACESTYLE
@property (readonly) UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0), ios(12.0));
#endif
@property (readonly) UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0));
@property (readonly) CGSize containerSize;
- (BOOL)isEqualToTraitCollection:(ASTraitCollection *)traitCollection;
@end
/**
* These are internal helper methods. Should never be called by the framework users.
*/
@interface ASTraitCollection (PrimitiveTraits)
+ (ASTraitCollection *)traitCollectionWithASPrimitiveTraitCollection:(ASPrimitiveTraitCollection)traits NS_RETURNS_RETAINED;
- (ASPrimitiveTraitCollection)primitiveTraitCollection;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,50 @@
//
// ASWeakSet.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
//
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
/**
* A class similar to NSSet that stores objects weakly.
* Note that this class uses NSPointerFunctionsObjectPointerPersonality
* that is, it uses shifted pointer for hashing, and identity comparison for equality.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASWeakSet<__covariant ObjectType> : NSObject<NSFastEnumeration>
/// Returns YES if the receiver is empty, NO otherwise.
@property (nonatomic, readonly, getter=isEmpty) BOOL empty;
/// Returns YES if `object` is in the receiver, NO otherwise.
- (BOOL)containsObject:(ObjectType)object AS_WARN_UNUSED_RESULT;
/// Insets `object` into the set.
- (void)addObject:(ObjectType)object;
/// Removes object from the set.
- (void)removeObject:(ObjectType)object;
/// Removes all objects from the set.
- (void)removeAllObjects;
/// Returns a standard *retained* NSArray of all objects. Not free to generate, but useful for iterating over contents.
- (NSArray<ObjectType> *)allObjects AS_WARN_UNUSED_RESULT;
/**
* How many objects are contained in this set.
* NOTE: This computed property is O(N). Consider using the `empty` property.
*/
@property (nonatomic, readonly) NSUInteger count;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,71 @@
//
// AsyncDisplayKit.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
//
#ifndef MINIMAL_ASDK
#define MINIMAL_ASDK 1
#endif
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Ancestry.h>
#import <AsyncDisplayKit/ASDisplayNode+Convenience.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASConfiguration.h>
#import <AsyncDisplayKit/ASConfigurationDelegate.h>
#import <AsyncDisplayKit/ASConfigurationInternal.h>
#import <AsyncDisplayKit/ASControlNode.h>
#import <AsyncDisplayKit/ASEditableTextNode.h>
#import <AsyncDisplayKit/ASScrollNode.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASDimensionInternal.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/_ASAsyncTransaction.h>
#import <AsyncDisplayKit/_ASAsyncTransactionGroup.h>
#import <AsyncDisplayKit/_ASAsyncTransactionContainer.h>
#import <AsyncDisplayKit/ASCollections.h>
#import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/_ASDisplayView.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASTextNodeTypes.h>
#import <AsyncDisplayKit/ASBlockTypes.h>
#import <AsyncDisplayKit/ASContextTransitioning.h>
#import <AsyncDisplayKit/ASControlNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASHashing.h>
#import <AsyncDisplayKit/ASLocking.h>
#import <AsyncDisplayKit/ASMainThreadDeallocation.h>
#import <AsyncDisplayKit/ASRunLoopQueue.h>
#import <AsyncDisplayKit/ASTextKitComponents.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASWeakSet.h>
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
#import <AsyncDisplayKit/UIView+ASConvenience.h>
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/NSArray+Diffing.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/UIResponder+AsyncDisplayKit.h>
#import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
#import <AsyncDisplayKit/_ASTransitionContext.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASCGImageBuffer.h>
#import <AsyncDisplayKit/ASControlTargetAction.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
@@ -0,0 +1,51 @@
//
// CoreGraphics+ASConvenience.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreGraphics/CoreGraphics.h>
#import <tgmath.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#ifndef CGFLOAT_EPSILON
#if CGFLOAT_IS_DOUBLE
#define CGFLOAT_EPSILON DBL_EPSILON
#else
#define CGFLOAT_EPSILON FLT_EPSILON
#endif
#endif
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_INLINE CGFloat ASCGFloatFromString(NSString *string)
{
#if CGFLOAT_IS_DOUBLE
return string.doubleValue;
#else
return string.floatValue;
#endif
}
ASDISPLAYNODE_INLINE CGFloat ASCGFloatFromNumber(NSNumber *number)
{
#if CGFLOAT_IS_DOUBLE
return number.doubleValue;
#else
return number.floatValue;
#endif
}
ASDISPLAYNODE_INLINE BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
{
return fabs(size1.width - size2.width) < delta && fabs(size1.height - size2.height) < delta;
};
NS_ASSUME_NONNULL_END
@@ -0,0 +1,92 @@
//
// NSArray+Diffing.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
//
#import <Foundation/Foundation.h>
/**
* These changes can be used to transform `self` to `array` by applying them in (any) order, *without shifting* the
* other elements. This can be done (in an NSMutableArray) by calling `setObject:atIndexedSubscript:` (or just use
* [subscripting] directly) for insertions from `array` into `self` (not the seemingly more apt `insertObject:atIndex`!),
* and using the same method for deletions from `self` (*set* a `[NSNull null]` as opposed to `removeObject:atIndex:`).
* After all inserts/deletes have been applied, there will be no nulls left (except possibly at the end of the array if
* `[array count] < [self count]`)
* Some examples:
* in: ab c
* out: abdc
* diff: ..+.
*
* in: abcd
* out: dcba
* dif: ---.+++
*
* in: abcd
* out: ab d
* diff: ..-.
*
* in: a bcd
* out: adbc
* diff: .+..-
*
* If `moves` pointer is passed in, instances where one element moves to another location are detected and reported,
* possibly replacing pairs of delete/insert. The process for transforming an array remains the same, however now it is
* important to apply the moves in order and not overwrite an element that needs to be moved somewhere else.
*
* the same examples, with moves:
* in: ab c
* out: abdc
* diff: ..+.
*
* in: abcd
* out: dcba
* diff: 321.
*
* in: abcd
* out: ab d
* diff: ..-.
*
* in: abcd
* out: adbc
* diff: .312
*
* Other notes:
*
* No index will be both moved from and deleted.
* Each index 0...[self count] will be either moved from or deleted. If it is moved to the same location, we omit it.
* Each index 0...[array count] will be the destination of ONE move or ONE insert.
* Knowing these things means any two of the three (delete, move, insert) implies the third.
*/
@interface NSArray (Diffing)
/**
* @abstract Compares two arrays, providing the insertion and deletion indexes needed to transform into the target array.
* @discussion This compares the equality of each object with `isEqual:`.
* This diffing algorithm uses a bottom-up memoized longest common subsequence solution to identify differences.
* It runs in O(mn) complexity.
*/
- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions;
/**
* @abstract Compares two arrays, providing the insertion and deletion indexes needed to transform into the target array.
* @discussion The `compareBlock` is used to identify the equality of the objects within the arrays.
* This diffing algorithm uses a bottom-up memoized longest common subsequence solution to identify differences.
* It runs in O(mn) complexity.
*/
- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions compareBlock:(BOOL (^)(id lhs, id rhs))comparison;
/**
* @abstract Compares two arrays, providing the insertion, deletion, and move indexes needed to transform into the target array.
* @discussion This compares the equality of each object with `isEqual:`.
* This diffing algorithm uses a bottom-up memoized longest common subsequence solution to identify differences.
* It runs in O(mn) complexity.
* The moves are returned in ascending order of their destination index.
*/
- (void)asdk_diffWithArray:(NSArray *)array insertions:(NSIndexSet **)insertions deletions:(NSIndexSet **)deletions moves:(NSArray<NSIndexPath *> **)moves;
@end
@@ -0,0 +1,25 @@
//
// UIResponder+AsyncDisplayKit.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
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIResponder (AsyncDisplayKit)
/**
* The nearest view controller above this responder, if one exists.
*
* This property must be accessed on the main thread.
*/
@property (nonatomic, nullable, readonly) __kindof UIViewController *asdk_associatedViewController;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,99 @@
//
// UIView+ASConvenience.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
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/**
These are the properties we support from CALayer (implemented in the pending state)
*/
@protocol ASDisplayProperties <NSObject>
@property (nonatomic) CGPoint position;
@property (nonatomic) CGFloat zPosition;
@property (nonatomic) CGPoint anchorPoint;
@property (nonatomic) CGFloat cornerRadius;
@property (nullable, nonatomic) id contents;
@property (nonatomic, copy) NSString *contentsGravity;
@property (nonatomic) CGRect contentsRect;
@property (nonatomic) CGRect contentsCenter;
@property (nonatomic) CGFloat contentsScale;
@property (nonatomic) CGFloat rasterizationScale;
@property (nonatomic) CATransform3D transform;
@property (nonatomic) CATransform3D sublayerTransform;
@property (nonatomic) BOOL needsDisplayOnBoundsChange;
@property (nonatomic) __attribute__((NSObject)) CGColorRef shadowColor;
@property (nonatomic) CGFloat shadowOpacity;
@property (nonatomic) CGSize shadowOffset;
@property (nonatomic) CGFloat shadowRadius;
@property (nonatomic) CGFloat borderWidth;
@property (nonatomic, getter = isOpaque) BOOL opaque;
@property (nonatomic) __attribute__((NSObject)) CGColorRef borderColor;
@property (nonatomic) __attribute__((NSObject)) CGColorRef backgroundColor;
@property (nonatomic) BOOL allowsGroupOpacity;
@property (nonatomic) BOOL allowsEdgeAntialiasing;
@property (nonatomic) unsigned int edgeAntialiasingMask;
- (void)setNeedsDisplay;
- (void)setNeedsLayout;
- (void)layoutIfNeeded;
@end
/**
These are all of the "good" properties of the UIView API that we support in pendingViewState or view of an ASDisplayNode.
*/
@protocol ASDisplayNodeViewProperties
@property (nonatomic) BOOL clipsToBounds;
@property (nonatomic, getter=isHidden) BOOL hidden;
@property (nonatomic) BOOL autoresizesSubviews;
@property (nonatomic) UIViewAutoresizing autoresizingMask;
@property (nonatomic, null_resettable) UIColor *tintColor;
@property (nonatomic) CGFloat alpha;
@property (nonatomic) CGRect bounds;
@property (nonatomic) CGRect frame; // Only for use with nodes wrapping synchronous views
@property (nonatomic) UIViewContentMode contentMode;
@property (nonatomic) UISemanticContentAttribute semanticContentAttribute API_AVAILABLE(ios(9.0), tvos(9.0));
@property (nonatomic, getter=isUserInteractionEnabled) BOOL userInteractionEnabled;
@property (nonatomic, getter=isExclusiveTouch) BOOL exclusiveTouch;
@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer;
@property (nonatomic) UIEdgeInsets layoutMargins;
@property (nonatomic) BOOL preservesSuperviewLayoutMargins;
@property (nonatomic) BOOL insetsLayoutMarginsFromSafeArea;
/**
Following properties of the UIAccessibility informal protocol are supported as well.
We don't declare them here, so _ASPendingState does not complain about them being not implemented,
as they are already on NSObject
@property (nonatomic) BOOL isAccessibilityElement;
@property (nonatomic, copy, nullable) NSString *accessibilityLabel;
@property (nonatomic, copy, nullable) NSAttributedString *accessibilityAttributedLabel API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, copy, nullable) NSString *accessibilityHint;
@property (nonatomic, copy, nullable) NSAttributedString *accessibilityAttributedHint API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic, copy, nullable) NSString *accessibilityValue;
@property (nonatomic, copy, nullable) NSAttributedString *accessibilityAttributedValue API_AVAILABLE(ios(11.0),tvos(11.0));
@property (nonatomic) UIAccessibilityTraits accessibilityTraits;
@property (nonatomic) CGRect accessibilityFrame;
@property (nonatomic, nullable) NSString *accessibilityLanguage;
@property (nonatomic) BOOL accessibilityElementsHidden;
@property (nonatomic) BOOL accessibilityViewIsModal;
@property (nonatomic) BOOL shouldGroupAccessibilityChildren;
*/
// Accessibility identification support
@property (nullable, nonatomic, copy) NSString *accessibilityIdentifier;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,120 @@
//
// _ASAsyncTransaction.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
#define ASDISPLAYNODE_DELAY_DISPLAY 0
@class _ASAsyncTransaction;
typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled);
typedef id<NSObject> _Nullable(^asyncdisplaykit_async_transaction_operation_block_t)(void);
typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id _Nullable value, BOOL canceled);
/**
State is initially ASAsyncTransactionStateOpen.
Every transaction MUST be committed. It is an error to fail to commit a transaction.
A committed transaction MAY be canceled. You cannot cancel an open (uncommitted) transaction.
*/
typedef NS_ENUM(NSUInteger, ASAsyncTransactionState) {
ASAsyncTransactionStateOpen = 0,
ASAsyncTransactionStateCommitted,
ASAsyncTransactionStateCanceled,
ASAsyncTransactionStateComplete
};
AS_EXTERN NSInteger const ASDefaultTransactionPriority;
/**
@summary ASAsyncTransaction provides lightweight transaction semantics for asynchronous operations.
@desc ASAsyncTransaction provides the following properties:
- Transactions group an arbitrary number of operations, each consisting of an execution block and a completion block.
- The execution block returns a single object that will be passed to the completion block.
- Execution blocks added to a transaction will run in parallel on the global background dispatch queues;
the completion blocks are dispatched to the callback queue.
- Every operation completion block is guaranteed to execute, regardless of cancelation.
However, execution blocks may be skipped if the transaction is canceled.
- Operation completion blocks are always executed in the order they were added to the transaction, assuming the
callback queue is serial of course.
*/
@interface _ASAsyncTransaction : NSObject
/**
@summary Initialize a transaction that can start collecting async operations.
@param completionBlock A block that is called when the transaction is completed.
*/
- (instancetype)initWithCompletionBlock:(nullable asyncdisplaykit_async_transaction_completion_block_t)completionBlock;
/**
@summary Block the main thread until the transaction is complete, including callbacks.
@desc This must be called on the main thread.
*/
- (void)waitUntilComplete;
/**
A block that is called when the transaction is completed.
*/
@property (nullable, readonly) asyncdisplaykit_async_transaction_completion_block_t completionBlock;
/**
The state of the transaction.
@see ASAsyncTransactionState
*/
@property (readonly) ASAsyncTransactionState state;
/**
@summary Adds a synchronous operation to the transaction. The execution block will be executed immediately.
@desc The block will be executed on the specified queue and is expected to complete synchronously. The async
transaction will wait for all operations to execute on their appropriate queues, so the blocks may still be executing
async if they are running on a concurrent queue, even though the work for this block is synchronous.
@param block The execution block that will be executed on a background queue. This is where the expensive work goes.
@param priority Execution priority; Tasks with higher priority will be executed sooner
@param queue The dispatch queue on which to execute the block.
@param completion The completion block that will be executed with the output of the execution block when all of the
operations in the transaction are completed. Executed and released on callbackQueue.
*/
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
priority:(NSInteger)priority
queue:(dispatch_queue_t)queue
completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
/**
@summary Cancels all operations in the transaction.
@desc You can only cancel a committed transaction.
All completion blocks are always called, regardless of cancelation. Execution blocks may be skipped if canceled.
*/
- (void)cancel;
/**
@summary Marks the end of adding operations to the transaction.
@desc You MUST commit every transaction you create. It is an error to create a transaction that is never committed.
When all of the operations that have been added have completed the transaction will execute their completion
blocks.
If no operations were added to this transaction, invoking commit will execute the transaction's completion block synchronously.
*/
- (void)commit;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,81 @@
//
// _ASAsyncTransactionContainer.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
//
#pragma once
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@class _ASAsyncTransaction;
typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) {
/**
The async container has no outstanding transactions.
Whatever it is displaying is up-to-date.
*/
ASAsyncTransactionContainerStateNoTransactions = 0,
/**
The async container has one or more outstanding async transactions.
Its contents may be out of date or showing a placeholder, depending on the configuration of the contained ASDisplayLayers.
*/
ASAsyncTransactionContainerStatePendingTransactions,
};
@protocol ASAsyncTransactionContainer
/**
@summary If YES, the receiver is marked as a container for async transactions, grouping all of the transactions
in the container hierarchy below the receiver together in a single ASAsyncTransaction.
@default NO
*/
@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter=asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer;
/**
@summary The current state of the receiver; indicates if it is currently performing asynchronous operations or if all operations have finished/canceled.
*/
@property (nonatomic, readonly) ASAsyncTransactionContainerState asyncdisplaykit_asyncTransactionContainerState;
/**
@summary Cancels all async transactions on the receiver.
*/
- (void)asyncdisplaykit_cancelAsyncTransactions;
@property (nullable, nonatomic, setter=asyncdisplaykit_setCurrentAsyncTransaction:) _ASAsyncTransaction *asyncdisplaykit_currentAsyncTransaction;
@end
@interface CALayer (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
/**
@summary Returns the current async transaction for this layer. A new transaction is created if one
did not already exist. This method will always return an open, uncommitted transaction.
@desc asyncdisplaykit_asyncTransactionContainer does not need to be YES for this to return a transaction.
Defaults to nil.
*/
@property (nullable, nonatomic, readonly) _ASAsyncTransaction *asyncdisplaykit_asyncTransaction;
/**
@summary Goes up the superlayer chain until it finds the first layer with asyncdisplaykit_asyncTransactionContainer=YES (including the receiver) and returns it.
Returns nil if no parent container is found.
*/
@property (nullable, nonatomic, readonly) CALayer *asyncdisplaykit_parentTransactionContainer;
/**
@summary Whether or not this layer should serve as a transaction container.
Defaults to NO.
*/
@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer;
@end
@interface UIView (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,37 @@
//
// _ASAsyncTransactionGroup.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@protocol ASAsyncTransactionContainer;
/// A group of transaction containers, for which the current transactions are committed together at the end of the next runloop tick.
AS_SUBCLASSING_RESTRICTED
@interface _ASAsyncTransactionGroup : NSObject
/// The main transaction group is scheduled to commit on every tick of the main runloop.
/// Access from the main thread only.
@property (class, nonatomic, readonly) _ASAsyncTransactionGroup *mainTransactionGroup;
- (void)commit;
/// Add a transaction container to be committed.
- (void)addTransactionContainer:(id<ASAsyncTransactionContainer>)container;
/// Use the main group.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,84 @@
//
// _ASCoreAnimationExtras.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNode.h>
// This protocol defines the core properties that ASDisplayNode and CALayer share, for managing contents.
@protocol ASResizableContents
@required
@property id contents;
@property CGRect contentsRect;
@property CGRect contentsCenter;
@property CGFloat contentsScale;
@property CGFloat rasterizationScale;
@property NSString *contentsGravity;
@end
@interface CALayer (ASResizableContents) <ASResizableContents>
@end
@interface ASDisplayNode (ASResizableContents) <ASResizableContents>
@end
/**
This function can operate on either an ASDisplayNode (including un-loaded) or CALayer directly. More info about resizing mode: https://github.com/TextureGroup/Texture/issues/439
@param obj ASDisplayNode, CALayer or object that conforms to `ASResizableContents` protocol
@param image Image you would like to resize
*/
AS_EXTERN void ASDisplayNodeSetResizableContents(id<ASResizableContents> obj, UIImage *image);
/**
Turns a value of UIViewContentMode to a string for debugging or serialization
@param contentMode Any of the UIViewContentMode constants
@return A human-readable representation of the constant, or the integer value of the constant if not recognized.
*/
AS_EXTERN NSString *ASDisplayNodeNSStringFromUIContentMode(UIViewContentMode contentMode);
/**
Turns a string representing a contentMode into a contentMode
@param string Any of the strings in UIContentModeDescriptionLUT
@return Any of the UIViewContentMode constants, or an int if the string is a number. If the string is not recognized, UIViewContentModeScaleToFill is returned.
*/
AS_EXTERN UIViewContentMode ASDisplayNodeUIContentModeFromNSString(NSString *string);
/**
Maps a value of UIViewContentMode to a corresponding contentsGravity
It is worth noting that UIKit and CA have inverse definitions of "top" and "bottom" on iOS, so the corresponding contentsGravity for UIViewContentModeTopLeft is kCAContentsGravityBottomLeft
@param contentMode A content mode except for UIViewContentModeRedraw, which has no corresponding contentsGravity (it corresponds to needsDisplayOnBoundsChange = YES)
@return An NSString constant from the documentation, eg kCAGravityCenter... or nil if there is no corresponding contentsGravity. Will assert if contentMode is unknown.
*/
AS_EXTERN NSString *const ASDisplayNodeCAContentsGravityFromUIContentMode(UIViewContentMode contentMode);
/**
Maps a value of contentsGravity to a corresponding UIViewContentMode
It is worth noting that UIKit and CA have inverse definitions of "top" and "bottom" on iOS, so the corresponding contentMode for kCAContentsGravityBottomLeft is UIViewContentModeTopLeft
@param contentsGravity A contents gravity
@return A UIViewContentMode constant from UIView.h, eg UIViewContentModeCenter..., or UIViewContentModeScaleToFill if contentsGravity is not one of the CA constants. Will assert if the contentsGravity is unknown.
*/
AS_EXTERN UIViewContentMode ASDisplayNodeUIContentModeFromCAContentsGravity(NSString *const contentsGravity);
/**
Use this to create a stretchable appropriate to approximate a filled rectangle, but with antialiasing on the edges when not pixel-aligned. It's best to keep the layer this image is added to with contentsScale equal to the scale of the final transform to screen space so it is able to antialias appropriately even when you shrink or grow the layer.
@param color the fill color to use in the center of the image
@param innerSize Unfortunately, 4 seems to be the smallest inner size that works if you're applying this stretchable to a larger box, whereas it does not display correctly for larger boxes. Thus some adjustment is necessary for the size of box you're displaying. If you're showing a 1px horizontal line, pass 1 height and at least 4 width. 2px vertical line: 2px wide, 4px high. Passing an innerSize greater that you desire is wasteful
*/
AS_EXTERN UIImage *ASDisplayNodeStretchableBoxContentsWithColor(UIColor *color, CGSize innerSize);
/**
Checks whether a layer has ongoing animations
@param layer A layer to check if animations are ongoing
@return YES if the layer has ongoing animations, otherwise NO
*/
AS_EXTERN BOOL ASDisplayNodeLayerHasAnimations(CALayer *layer);
// This function is a less generalized version of ASDisplayNodeSetResizableContents.
AS_EXTERN void ASDisplayNodeSetupLayerContentsWithResizableImage(CALayer *layer, UIImage *image) ASDISPLAYNODE_DEPRECATED;
@@ -0,0 +1,149 @@
//
// _ASDisplayLayer.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
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASBlockTypes.h>
NS_ASSUME_NONNULL_BEGIN
@class ASDisplayNode;
@protocol _ASDisplayLayerDelegate;
@interface _ASDisplayLayer : CALayer
/**
@discussion This property overrides the CALayer category method which implements this via associated objects.
This should result in much better performance for _ASDisplayLayers.
*/
@property (nullable, nonatomic, weak) ASDisplayNode *asyncdisplaykit_node;
/**
@summary Set to YES to enable asynchronous display for the receiver.
@default YES (note that this might change for subclasses)
*/
@property (nonatomic) BOOL displaysAsynchronously;
/**
@summary Cancels any pending async display.
@desc If the receiver has had display called and is waiting for the dispatched async display to be executed, this will
cancel that dispatched async display. This method is useful to call when removing the receiver from the window.
*/
- (void)cancelAsyncDisplay;
/**
@summary The dispatch queue used for async display.
@desc This is exposed here for tests only.
*/
+ (dispatch_queue_t)displayQueue;
/**
@summary Delegate for asynchronous display of the layer. This should be the node (default) unless you REALLY know what you're doing.
@desc The asyncDelegate will have the opportunity to override the methods related to async display.
*/
@property (nullable, weak) id<_ASDisplayLayerDelegate> asyncDelegate;
/**
@summary Suspends both asynchronous and synchronous display of the receiver if YES.
@desc This can be used to suspend all display calls while the receiver is still in the view hierarchy. If you
want to just cancel pending async display, use cancelAsyncDisplay instead.
@default NO
*/
@property (nonatomic, getter=isDisplaySuspended) BOOL displaySuspended;
/**
@summary Bypasses asynchronous rendering and performs a blocking display immediately on the current thread.
@desc Used by ASDisplayNode to display the layer synchronously on-demand (must be called on the main thread).
*/
- (void)displayImmediately;
@end
/**
* Optional methods that the view associated with an _ASDisplayLayer can implement.
* This is distinguished from _ASDisplayLayerDelegate in that it points to the _view_
* not the node. Unfortunately this is required by ASCollectionView, since we currently
* can't guarantee that an ASCollectionNode exists for it.
*/
@protocol ASCALayerExtendedDelegate
@optional
- (void)layer:(CALayer *)layer didChangeBoundsWithOldValue:(CGRect)oldBounds newValue:(CGRect)newBounds;
@end
/**
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.
display/drawRect implementations MUST be thread-safe, as they can be called on the displayQueue (async) or the main thread (sync/displayImmediately)
*/
@protocol _ASDisplayLayerDelegate <NSObject>
@optional
// Called on the display queue and/or main queue (MUST BE THREAD SAFE)
/**
@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 isCancelledBlock 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.
*/
+ (void)drawRect:(CGRect)bounds
withParameters:(nullable id)parameters
isCancelled:(AS_NOESCAPE 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 isCancelledBlock 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 (backed by a CGImage) with contents that are ready to display on the main thread. Make sure that the image is already decoded before returning it here.
*/
+ (UIImage *)displayWithParameters:(nullable id<NSObject>)parameters
isCancelled:(AS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelledBlock;
// Called on the main thread only
/**
@summary Delegate override for drawParameters
*/
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer;
/**
@summary Delegate override for willDisplay
*/
- (void)willDisplayAsyncLayer:(_ASDisplayLayer *)layer asynchronously:(BOOL)asynchronously;
/**
@summary Delegate override for didDisplay
*/
- (void)didDisplayAsyncLayer:(_ASDisplayLayer *)layer;
/**
@summary Delegate callback to display a layer, synchronously or asynchronously. 'asyncLayer' does not necessarily need to exist (can be nil). Typically, a delegate will display/draw its own contents and then set .contents on the layer when finished.
*/
- (void)displayAsyncLayer:(_ASDisplayLayer *)asyncLayer asynchronously:(BOOL)asynchronously;
/**
@summary Delegate callback to handle a layer which requests its asynchronous display be cancelled.
*/
- (void)cancelDisplayAsyncLayer:(_ASDisplayLayer *)asyncLayer;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,44 @@
//
// _ASDisplayView.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
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
// This class is only for use by ASDisplayNode and should never be subclassed or used directly.
// Note that the "node" property is added to UIView directly via a category in ASDisplayNode.
@class ASDisplayNode;
@interface _ASDisplayView : UIView
/**
@discussion This property overrides the UIView category method which implements this via associated objects.
This should result in much better performance for _ASDisplayView.
*/
@property (nullable, nonatomic, weak) ASDisplayNode *asyncdisplaykit_node;
// These methods expose a way for ASDisplayNode touch events to let the view call super touch events
// Some UIKit mechanisms, like UITableView and UICollectionView selection handling, require this to work
- (void)__forwardTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)__forwardTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)__forwardTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)__forwardTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
// These methods expose a way for ASDisplayNode responder methods to let the view call super responder methods
// They are called from ASDisplayNode to pass through UIResponder methods to the view
- (BOOL)__canBecomeFirstResponder;
- (BOOL)__becomeFirstResponder;
- (BOOL)__canResignFirstResponder;
- (BOOL)__resignFirstResponder;
- (BOOL)__isFirstResponder;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,50 @@
//
// _ASTransitionContext.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
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASContextTransitioning.h>
@class ASLayout;
@class _ASTransitionContext;
@protocol _ASTransitionContextLayoutDelegate <NSObject>
- (NSArray<ASDisplayNode *> *)currentSubnodesWithTransitionContext:(_ASTransitionContext *)context;
- (NSArray<ASDisplayNode *> *)insertedSubnodesWithTransitionContext:(_ASTransitionContext *)context;
- (NSArray<ASDisplayNode *> *)removedSubnodesWithTransitionContext:(_ASTransitionContext *)context;
- (ASLayout *)transitionContext:(_ASTransitionContext *)context layoutForKey:(NSString *)key;
- (ASSizeRange)transitionContext:(_ASTransitionContext *)context constrainedSizeForKey:(NSString *)key;
@end
@protocol _ASTransitionContextCompletionDelegate <NSObject>
- (void)transitionContext:(_ASTransitionContext *)context didComplete:(BOOL)didComplete;
@end
@interface _ASTransitionContext : NSObject <ASContextTransitioning>
@property (nonatomic, readonly, getter=isAnimated) BOOL animated;
- (instancetype)initWithAnimation:(BOOL)animated
layoutDelegate:(id<_ASTransitionContextLayoutDelegate>)layoutDelegate
completionDelegate:(id<_ASTransitionContextCompletionDelegate>)completionDelegate;
@end
@interface _ASAnimatedTransitionContext : NSObject
@property (nonatomic, readonly) ASDisplayNode *node;
@property (nonatomic, readonly) CGFloat alpha;
+ (instancetype)contextForNode:(ASDisplayNode *)node alpha:(CGFloat)alphaValue NS_RETURNS_RETAINED;
@end