This commit is contained in:
Ali 2020-02-03 17:31:09 +04:00
parent 8385c4d331
commit 97caa448c2
233 changed files with 1665 additions and 12663 deletions

View File

@ -1,3 +1,5 @@
build --strategy=Genrule=local
build --apple_platform_type=ios
build --cxxopt='-std=c++14'
build --copt='-w'
build --swiftcopt='-Xcc' --swiftcopt='-w'

View File

@ -50,7 +50,7 @@ swift_library(
"//submodules/WalletCore:WalletCore",
"//submodules/BuildConfig:BuildConfig",
"//submodules/AppBundle:AppBundle",
#"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
#"//submodules/WalletUI:WalletUI",
],
)

View File

@ -1,10 +1,14 @@
load(
"@rules_apple_extras//apple:objc_library.bzl",
"objc_library",
)
public_headers = glob([
"Source/Public/AsyncDisplayKit/*.h",
"Source/PublicHeaders/AsyncDisplayKit/*.h",
])
private_headers = glob([
"Source/Implementation/AsyncDisplayKit/*.h",
"Source/*.h",
])
objc_library(
@ -19,14 +23,6 @@ objc_library(
defines = [
"MINIMAL_ASDK",
],
copts = [
"-w",
"-isystem",
"submodules/AsyncDisplayKit/Source/Implementation",
],
includes = [
"Source/Public",
],
sdk_frameworks = [
"Foundation",
"UIKit",

View File

@ -0,0 +1,186 @@
//
// ASAsciiArtBoxCreator.mm
// 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/ASAsciiArtBoxCreator.h>
#import <CoreGraphics/CoreGraphics.h>
#import <cmath>
static const NSUInteger kDebugBoxPadding = 2;
typedef NS_ENUM(NSUInteger, PIDebugBoxPaddingLocation)
{
PIDebugBoxPaddingLocationFront,
PIDebugBoxPaddingLocationEnd,
PIDebugBoxPaddingLocationBoth
};
@interface NSString(PIDebugBox)
@end
@implementation NSString(PIDebugBox)
+ (instancetype)debugbox_stringWithString:(NSString *)stringToRepeat repeatedCount:(NSUInteger)repeatCount NS_RETURNS_RETAINED
{
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[stringToRepeat length] * repeatCount];
for (NSUInteger index = 0; index < repeatCount; index++) {
[string appendString:stringToRepeat];
}
return [string copy];
}
- (NSString *)debugbox_stringByAddingPadding:(NSString *)padding count:(NSUInteger)count location:(PIDebugBoxPaddingLocation)location
{
NSString *paddingString = [NSString debugbox_stringWithString:padding repeatedCount:count];
switch (location) {
case PIDebugBoxPaddingLocationFront:
return [NSString stringWithFormat:@"%@%@", paddingString, self];
case PIDebugBoxPaddingLocationEnd:
return [NSString stringWithFormat:@"%@%@", self, paddingString];
case PIDebugBoxPaddingLocationBoth:
return [NSString stringWithFormat:@"%@%@%@", paddingString, self, paddingString];
}
return [self copy];
}
@end
@implementation ASAsciiArtBoxCreator
+ (NSString *)horizontalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent
{
if ([children count] == 0) {
return parent;
}
NSMutableArray *childrenLines = [NSMutableArray array];
// split the children into lines
NSUInteger lineCountPerChild = 0;
for (NSString *child in children) {
NSArray *lines = [child componentsSeparatedByString:@"\n"];
lineCountPerChild = MAX(lineCountPerChild, [lines count]);
}
for (NSString *child in children) {
NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy];
NSUInteger topPadding = ceil((CGFloat)(lineCountPerChild - [lines count])/2.0);
NSUInteger bottomPadding = (lineCountPerChild - [lines count])/2.0;
NSUInteger lineLength = [lines[0] length];
for (NSUInteger index = 0; index < topPadding; index++) {
[lines insertObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength] atIndex:0];
}
for (NSUInteger index = 0; index < bottomPadding; index++) {
[lines addObject:[NSString debugbox_stringWithString:@" " repeatedCount:lineLength]];
}
[childrenLines addObject:lines];
}
NSMutableArray *concatenatedLines = [NSMutableArray array];
NSString *padding = [NSString debugbox_stringWithString:@" " repeatedCount:kDebugBoxPadding];
for (NSUInteger index = 0; index < lineCountPerChild; index++) {
NSMutableString *line = [[NSMutableString alloc] init];
[line appendFormat:@"|%@",padding];
for (NSArray *childLines in childrenLines) {
[line appendFormat:@"%@%@", childLines[index], padding];
}
[line appendString:@"|"];
[concatenatedLines addObject:line];
}
// surround the lines in a box
NSUInteger totalLineLength = [concatenatedLines[0] length];
if (totalLineLength < [parent length]) {
NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - totalLineLength;
NSUInteger leftPadding = ceil((CGFloat)difference/2.0);
NSUInteger rightPadding = difference/2;
NSString *leftString = [@"|" debugbox_stringByAddingPadding:@" " count:leftPadding location:PIDebugBoxPaddingLocationEnd];
NSString *rightString = [@"|" debugbox_stringByAddingPadding:@" " count:rightPadding location:PIDebugBoxPaddingLocationFront];
NSMutableArray *paddedLines = [NSMutableArray array];
for (NSString *line in concatenatedLines) {
NSString *paddedLine = [line stringByReplacingOccurrencesOfString:@"|" withString:leftString options:NSCaseInsensitiveSearch range:NSMakeRange(0, 1)];
paddedLine = [paddedLine stringByReplacingOccurrencesOfString:@"|" withString:rightString options:NSCaseInsensitiveSearch range:NSMakeRange([paddedLine length] - 1, 1)];
[paddedLines addObject:paddedLine];
}
concatenatedLines = paddedLines;
// totalLineLength += difference;
}
concatenatedLines = [self appendTopAndBottomToBoxString:concatenatedLines parent:parent];
return [concatenatedLines componentsJoinedByString:@"\n"];
}
+ (NSString *)verticalBoxStringForChildren:(NSArray *)children parent:(NSString *)parent
{
if ([children count] == 0) {
return parent;
}
NSMutableArray *childrenLines = [NSMutableArray array];
NSUInteger maxChildLength = 0;
for (NSString *child in children) {
NSArray *lines = [child componentsSeparatedByString:@"\n"];
maxChildLength = MAX(maxChildLength, [lines[0] length]);
}
NSUInteger rightPadding = 0;
NSUInteger leftPadding = 0;
if (maxChildLength < [parent length]) {
NSUInteger difference = [parent length] + (2 * kDebugBoxPadding) - maxChildLength;
leftPadding = ceil((CGFloat)difference/2.0);
rightPadding = difference/2;
}
NSString *rightPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightPadding + kDebugBoxPadding];
NSString *leftPaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftPadding + kDebugBoxPadding];
for (NSString *child in children) {
NSMutableArray *lines = [[child componentsSeparatedByString:@"\n"] mutableCopy];
NSUInteger leftLinePadding = ceil((CGFloat)(maxChildLength - [lines[0] length])/2.0);
NSUInteger rightLinePadding = (maxChildLength - [lines[0] length])/2.0;
for (NSString *line in lines) {
NSString *rightLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:rightLinePadding];
rightLinePaddingString = [NSString stringWithFormat:@"%@%@|", rightLinePaddingString, rightPaddingString];
NSString *leftLinePaddingString = [NSString debugbox_stringWithString:@" " repeatedCount:leftLinePadding];
leftLinePaddingString = [NSString stringWithFormat:@"|%@%@", leftLinePaddingString, leftPaddingString];
NSString *paddingLine = [NSString stringWithFormat:@"%@%@%@", leftLinePaddingString, line, rightLinePaddingString];
[childrenLines addObject:paddingLine];
}
}
childrenLines = [self appendTopAndBottomToBoxString:childrenLines parent:parent];
return [childrenLines componentsJoinedByString:@"\n"];
}
+ (NSMutableArray *)appendTopAndBottomToBoxString:(NSMutableArray *)boxStrings parent:(NSString *)parent
{
NSUInteger totalLineLength = [boxStrings[0] length];
[boxStrings addObject:[NSString debugbox_stringWithString:@"-" repeatedCount:totalLineLength]];
NSUInteger leftPadding = ceil(((CGFloat)(totalLineLength - [parent length]))/2.0);
NSUInteger rightPadding = (totalLineLength - [parent length])/2;
NSString *topLine = [parent debugbox_stringByAddingPadding:@"-" count:leftPadding location:PIDebugBoxPaddingLocationFront];
topLine = [topLine debugbox_stringByAddingPadding:@"-" count:rightPadding location:PIDebugBoxPaddingLocationEnd];
[boxStrings insertObject:topLine atIndex:0];
return boxStrings;
}
@end

View File

@ -6,7 +6,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASCGImageBuffer.h>
#import "ASCGImageBuffer.h"
#import <sys/mman.h>
#import <mach/mach_init.h>

View File

@ -1,5 +1,5 @@
//
// ASTextKitTailTruncater.h
// ASControlNode+Private.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@ -7,15 +7,12 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASControlNode.h>
#import <AsyncDisplayKit/ASTextKitTruncating.h>
@interface ASControlNode (Private)
#if AS_ENABLE_TEXTNODE
AS_SUBCLASSING_RESTRICTED
@interface ASTextKitTailTruncater : NSObject <ASTextKitTruncating>
#if TARGET_OS_TV
- (void)_pressDown;
#endif
@end
#endif

View File

@ -8,9 +8,11 @@
//
#import <AsyncDisplayKit/ASControlNode.h>
#import "ASControlNode+Private.h"
#import <AsyncDisplayKit/ASControlNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASImageNode.h>
#import "ASInternalHelpers.h"
#import <AsyncDisplayKit/ASControlTargetAction.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASThread.h>
@ -70,6 +72,7 @@ CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode);
@implementation ASControlNode
{
ASImageNode *_debugHighlightOverlay;
}
#pragma mark - Lifecycle
@ -490,4 +493,9 @@ CGRect _ASControlNodeGetExpandedBounds(ASControlNode *controlNode) {
// Subclass hook
}
#pragma mark - Debug
- (ASImageNode *)debugHighlightOverlay
{
return _debugHighlightOverlay;
}
@end

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASControlTargetAction.h>
#import "ASControlTargetAction.h"
@implementation ASControlTargetAction
{

View File

@ -6,7 +6,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASDispatch.h>
#import "ASDispatch.h"
#import <AsyncDisplayKit/ASConfigurationInternal.h>

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASDisplayNode+Ancestry.h>
#import "ASDisplayNode+Ancestry.h"
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>

View File

@ -7,15 +7,15 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
#import "_ASCoreAnimationExtras.h"
#import <AsyncDisplayKit/_ASAsyncTransaction.h>
#import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "ASDisplayNodeInternal.h"
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import "ASSignpost.h"
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
using AS::MutexLocker;

View File

@ -7,12 +7,12 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASDisplayNode+Convenience.h>
#import "ASDisplayNode+Convenience.h"
#import <UIKit/UIViewController.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import "ASResponderChainEnumerator.h"
@implementation ASDisplayNode (Convenience)

View File

@ -8,17 +8,15 @@
//
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASCollections.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "ASDisplayNodeInternal.h"
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASNodeController+Beta.h>
#import "ASLayoutElementStylePrivate.h"
#import <AsyncDisplayKit/NSArray+Diffing.h>
#import <AsyncDisplayKit/ASCollections.h>
using AS::MutexLocker;
@ -74,7 +72,7 @@ using AS::MutexLocker;
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
{
ASLockScopeSelf();
ASScopedLockSelfOrToRoot();
// If one or multiple layout transitions are in flight it still can happen that layout information is requested
// on other threads. As the pending and calculated layout to be updated in the layout transition in here just a
@ -96,7 +94,6 @@ using AS::MutexLocker;
layout = [self calculateLayoutThatFits:constrainedSize
restrictedToSize:self.style.size
relativeToParentSize:parentSize];
as_log_verbose(ASLayoutLog(), "Established pending layout for %@ in %s", self, sel_getName(_cmd));
_pendingDisplayNodeLayout = ASDisplayNodeLayout(layout, constrainedSize, parentSize,version);
ASDisplayNodeAssertNotNil(layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newly calculated layout should not be nil! %@", self);
}
@ -229,8 +226,6 @@ ASLayoutElementStyleExtensibilityForwarding
ASDisplayNodeAssertThreadAffinity(self);
ASAssertUnlocked(__instanceLock__);
as_activity_create_for_scope("Set needs layout from above");
// Mark the node for layout in the next layout pass
[self setNeedsLayout];
@ -315,7 +310,7 @@ ASLayoutElementStyleExtensibilityForwarding
- (void)_u_measureNodeWithBoundsIfNecessary:(CGRect)bounds
{
// ASAssertUnlocked(__instanceLock__);
ASLockScopeSelf();
ASScopedLockSelfOrToRoot();
// Check if we are a subnode in a layout transition.
// In this case no measurement is needed as it's part of the layout transition
@ -345,13 +340,6 @@ ASLayoutElementStyleExtensibilityForwarding
if (!pendingLayoutIsPreferred && calculatedLayoutIsReusable) {
return;
}
as_activity_create_for_scope("Update node layout for current bounds");
as_log_verbose(ASLayoutLog(), "Node %@, bounds size %@, calculatedSize %@, calculatedIsDirty %d",
self,
NSStringFromCGSize(boundsSizeForLayout),
NSStringFromCGSize(_calculatedDisplayNodeLayout.layout.size),
_calculatedDisplayNodeLayout.version < _layoutVersion);
// _calculatedDisplayNodeLayout is not reusable we need to transition to a new one
[self cancelLayoutTransition];
@ -371,18 +359,13 @@ ASLayoutElementStyleExtensibilityForwarding
// If our bounds size is different than it, or invalid, recalculate. Use #define to avoid nullptr->
BOOL pendingLayoutApplicable = NO;
if (nextLayout.layout == nil) {
as_log_verbose(ASLayoutLog(), "No pending layout.");
} else if (!nextLayout.isValid(_layoutVersion)) {
as_log_verbose(ASLayoutLog(), "Pending layout is stale.");
} else if (layoutSizeDifferentFromBounds) {
as_log_verbose(ASLayoutLog(), "Pending layout size %@ doesn't match bounds size.", NSStringFromCGSize(nextLayout.layout.size));
} else {
as_log_verbose(ASLayoutLog(), "Using pending layout %@.", nextLayout.layout);
pendingLayoutApplicable = YES;
}
if (!pendingLayoutApplicable) {
as_log_verbose(ASLayoutLog(), "Measuring with previous constrained size.");
// Use the last known constrainedSize passed from a parent during layout (if never, use bounds).
NSUInteger version = _layoutVersion;
ASSizeRange constrainedSize = [self _locked_constrainedSizeForLayoutPass];
@ -403,7 +386,6 @@ ASLayoutElementStyleExtensibilityForwarding
// This can occur for either pre-calculated or newly-calculated layouts.
if (nextLayout.requestedLayoutFromAbove == NO
&& CGSizeEqualToSize(boundsSizeForLayout, nextLayout.layout.size) == NO) {
as_log_verbose(ASLayoutLog(), "Layout size doesn't match bounds size. Requesting layout from above.");
// The layout that we have specifies that this node (self) would like to be a different size
// than it currently is. Because that size has been computed within the constrainedSize, we
// expect that calling setNeedsLayoutFromAbove will result in our parent resizing us to this.
@ -578,13 +560,10 @@ ASLayoutElementStyleExtensibilityForwarding
measurementCompletion:(void(^)())completion
{
ASDisplayNodeAssertMainThread();
as_activity_create_for_scope("Transition node layout");
as_log_debug(ASLayoutLog(), "Transition layout for %@ sizeRange %@ anim %d asyncMeasure %d", self, NSStringFromASSizeRange(constrainedSize), animated, shouldMeasureAsync);
if (constrainedSize.max.width <= 0.0 || constrainedSize.max.height <= 0.0) {
// Using CGSizeZero for the sizeRange can cause negative values in client layout code.
// Most likely called transitionLayout: without providing a size, before first layout pass.
as_log_verbose(ASLayoutLog(), "Ignoring transition due to bad size range.");
return;
}
@ -612,12 +591,10 @@ ASLayoutElementStyleExtensibilityForwarding
// Every new layout transition has a transition id associated to check in subsequent transitions for cancelling
int32_t transitionID = [self _startNewTransition];
as_log_verbose(ASLayoutLog(), "Transition ID is %d", transitionID);
// NOTE: This block captures self. It's cheaper than hitting the weak table.
asdisplaynode_iscancelled_block_t isCancelled = ^{
BOOL result = (_transitionID != transitionID);
if (result) {
as_log_verbose(ASLayoutLog(), "Transition %d canceled, superseded by %d", transitionID, _transitionID.load());
}
return result;
};
@ -639,7 +616,7 @@ ASLayoutElementStyleExtensibilityForwarding
NSUInteger newLayoutVersion = _layoutVersion;
ASLayout *newLayout;
{
ASLockScopeSelf();
ASScopedLockSelfOrToRoot();
ASLayoutElementContext *ctx = [[ASLayoutElementContext alloc] init];
ctx.transitionID = transitionID;
@ -665,7 +642,6 @@ ASLayoutElementStyleExtensibilityForwarding
if (isCancelled()) {
return;
}
as_activity_create_for_scope("Commit layout transition");
ASLayoutTransition *pendingLayoutTransition;
_ASTransitionContext *pendingLayoutTransitionContext;
{
@ -693,7 +669,6 @@ ASLayoutElementStyleExtensibilityForwarding
// Apply complete layout transitions for all subnodes
{
as_activity_create_for_scope("Complete pending layout transitions for subtree");
ASDisplayNodePerformBlockOnEverySubnode(self, NO, ^(ASDisplayNode * _Nonnull node) {
[node _completePendingLayoutTransition];
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
@ -712,7 +687,6 @@ ASLayoutElementStyleExtensibilityForwarding
// Kick off animating the layout transition
{
as_activity_create_for_scope("Animate layout transition");
[self animateLayoutTransition:pendingLayoutTransitionContext];
}

View File

@ -9,13 +9,12 @@
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/_ASScopeTimer.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "_ASScopeTimer.h"
#import "ASDisplayNodeInternal.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import <AsyncDisplayKit/ASLayoutSpecPrivate.h>
#import <AsyncDisplayKit/ASLog.h>
#import "ASLayoutSpec+Subclasses.h"
#import "ASLayoutSpecPrivate.h"
#import <AsyncDisplayKit/ASThread.h>

View File

@ -7,13 +7,13 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
#import <AsyncDisplayKit/_ASPendingState.h>
#import "_ASCoreAnimationExtras.h"
#import "_ASPendingState.h"
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "ASDisplayNodeInternal.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASPendingStateController.h>
#import "ASPendingStateController.h"
/**
* The following macros are conveniences to help in the common tasks related to the bridging that ASDisplayNode does to UIView and CALayer.

View File

@ -7,43 +7,40 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import "ASDisplayNodeInternal.h"
#import <AsyncDisplayKit/ASDisplayNode+Ancestry.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASDisplayNode+LayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import "ASLayoutSpec+Subclasses.h"
#import <objc/runtime.h>
#include <string>
#import <AsyncDisplayKit/_ASAsyncTransaction.h>
#import <AsyncDisplayKit/_ASAsyncTransactionContainer+Private.h>
#import <AsyncDisplayKit/_ASCoreAnimationExtras.h>
#import "_ASAsyncTransactionContainer+Private.h"
#import "_ASCoreAnimationExtras.h"
#import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/_ASDisplayView.h>
#import <AsyncDisplayKit/_ASPendingState.h>
#import <AsyncDisplayKit/_ASScopeTimer.h>
#import "_ASPendingState.h"
#import "_ASScopeTimer.h"
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASDisplayNode+InterfaceState.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import "ASLayoutElementStylePrivate.h"
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutSpecPrivate.h>
#import <AsyncDisplayKit/ASLog.h>
#import "ASLayoutSpecPrivate.h"
#import <AsyncDisplayKit/ASMainThreadDeallocation.h>
#import <AsyncDisplayKit/ASNodeController+Beta.h>
#import <AsyncDisplayKit/ASRunLoopQueue.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import "ASSignpost.h"
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASWeakProxy.h>
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import "ASWeakProxy.h"
#import "ASResponderChainEnumerator.h"
// Conditionally time these scopes to our debug ivars (only exist in debug/profile builds)
#if TIME_DISPLAYNODE_OPS
@ -209,7 +206,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
// Subclasses should never override these. Use unused to prevent warnings
__unused NSString *classString = NSStringFromClass(self);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString);
//ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method. Instead override calculateLayoutThatFits:.", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method. Instead override calculateLayoutThatFits:.", classString);
@ -589,7 +586,6 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
ASDisplayNodeAssertMainThread();
ASAssertUnlocked(__instanceLock__);
ASDisplayNodeLogEvent(self, @"didLoad");
as_log_verbose(ASNodeLog(), "didLoad %@", self);
TIME_SCOPED(_debugTimeForDidLoad);
[self didLoad];
@ -893,18 +889,6 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
_automaticallyRelayoutOnLayoutMarginsChanges = flag;
}
- (void)__setNodeController:(ASNodeController *)controller
{
// See docs for why we don't lock.
if (controller.shouldInvertStrongReference) {
_strongNodeController = controller;
_weakNodeController = nil;
} else {
_weakNodeController = controller;
_strongNodeController = nil;
}
}
#pragma mark - UIResponder
#define HANDLE_NODE_RESPONDER_METHOD(__sel) \
@ -1056,7 +1040,6 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
// Performing layout on a zero-bounds view often results in frame calculations
// with negative sizes after applying margins, which will cause
// layoutThatFits: on subnodes to assert.
as_log_debug(OS_LOG_DISABLED, "Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
return;
}
@ -1066,8 +1049,6 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
return;
}
as_activity_create_for_scope("-[ASDisplayNode __layout]");
// 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).
l.unlock();
@ -1105,9 +1086,6 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
restrictedToSize:(ASLayoutElementSize)size
relativeToParentSize:(CGSize)parentSize
{
as_activity_scope_verbose(as_activity_create("Calculate node layout", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
as_log_verbose(ASLayoutLog(), "Calculating layout for %@ sizeRange %@", self, NSStringFromASSizeRange(constrainedSize));
#if AS_KDEBUG_ENABLE
// We only want one calculateLayout signpost interval per thread.
// Currently there is no fallback for profiling i386, since it's not useful.
@ -1120,7 +1098,6 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__);
ASSizeRange styleAndParentSize = ASLayoutElementSizeResolve(self.style.size, parentSize);
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, styleAndParentSize);
ASLayout *result = [self calculateLayoutThatFits:resolvedRange];
as_log_verbose(ASLayoutLog(), "Calculated layout %@", result);
#if AS_KDEBUG_ENABLE
if (--tls_callDepth == 0) {
@ -1351,7 +1328,6 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
}];
});
as_log_verbose(ASDisplayLog(), "%s %@", sel_getName(_cmd), node);
[renderQueue enqueue:node];
}
@ -1574,33 +1550,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
});
}
- (void)_setClipCornerLayersVisible:(BOOL)__unused visible
- (void)_setClipCornerLayersVisible:(BOOL)visible
{
/*
ASPerformBlockOnMainThread(^{
ASDisplayNodeAssertMainThread();
if (visible) {
for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) {
if (_clipCornerLayers[idx] == nil) {
static ASDisplayNodeCornerLayerDelegate *clipCornerLayers;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
clipCornerLayers = [[ASDisplayNodeCornerLayerDelegate alloc] init];
});
_clipCornerLayers[idx] = [[CALayer alloc] init];
_clipCornerLayers[idx].zPosition = 99999;
_clipCornerLayers[idx].delegate = clipCornerLayers;
}
}
[self _updateClipCornerLayerContentsWithRadius:_cornerRadius backgroundColor:self.backgroundColor];
} else {
for (int idx = 0; idx < NUM_CLIP_CORNER_LAYERS; idx++) {
[_clipCornerLayers[idx] removeFromSuperlayer];
_clipCornerLayers[idx] = nil;
}
}
});
*/
}
- (void)updateCornerRoundingWithType:(ASCornerRoundingType)newRoundingType cornerRadius:(CGFloat)newCornerRadius
@ -2110,8 +2061,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
// TODO: Disabled due to PR: https://github.com/TextureGroup/Texture/pull/1204
// ASAssertUnlocked(__instanceLock__);
as_log_verbose(ASNodeLog(), "Insert subnode %@ at index %zd of %@ and remove subnode %@", subnode, subnodeIndex, self, oldSubnode);
if (subnode == nil || subnode == self) {
ASDisplayNodeFailAssert(@"Cannot insert a nil subnode or self as subnode");
return;
@ -2548,7 +2497,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
// Note: we continue even if supernode is nil to ensure view/layer are removed from hierarchy.
if (supernode != nil) {
as_log_verbose(ASNodeLog(), "Remove %@ from supernode %@", self, supernode);
}
// Clear supernode's reference to us before removing the view from the hierarchy, as _ASDisplayView
@ -2828,7 +2776,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
}
ASDisplayNodeLogEvent(self, @"setHierarchyState: %@", NSStringFromASHierarchyStateChange(oldState, newState));
as_log_verbose(ASNodeLog(), "%s%@ %@", sel_getName(_cmd), NSStringFromASHierarchyStateChange(oldState, newState), self);
}
- (void)willEnterHierarchy
@ -2943,8 +2890,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
- (void)recursivelySetInterfaceState:(ASInterfaceState)newInterfaceState
{
as_activity_create_for_scope("Recursively set interface state");
// Instead of each node in the recursion assuming it needs to schedule itself for display,
// setInterfaceState: skips this when handling range-managed nodes (our whole subtree has this set).
// If our range manager intends for us to be displayed right now, and didn't before, get started!
@ -3105,7 +3050,6 @@ ASDISPLAYNODE_INLINE BOOL subtreeIsRasterized(ASDisplayNode *node) {
// for all cell nodes and it isn't currently meaningful.
BOOL measureChangeOnly = ((oldState | newState) == ASInterfaceStateMeasureLayout);
if (!measureChangeOnly) {
as_log_verbose(ASNodeLog(), "%s %@ %@", sel_getName(_cmd), NSStringFromASInterfaceStateChange(oldState, newState), self);
}
ASDisplayNodeLogEvent(self, @"interfaceStateDidChange: %@", NSStringFromASInterfaceStateChange(oldState, newState));

View File

@ -8,8 +8,8 @@
//
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import <AsyncDisplayKit/ASDisplayNode+Ancestry.h>
#import <queue>

View File

@ -16,7 +16,7 @@
#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASLayoutElement.h>
#import <AsyncDisplayKit/ASLayoutTransition.h>
#import "ASLayoutTransition.h"
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/_ASTransitionContext.h>
#import <AsyncDisplayKit/ASWeakSet.h>
@ -287,16 +287,6 @@ AS_EXTERN NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimest
*/
- (void)__setNeedsDisplay;
/**
* Setup the node -> controller reference. Strong or weak is based on
* the "shouldInvertStrongReference" property of the controller.
*
* Note: To prevent lock-ordering deadlocks, this method does not take the node's lock.
* In practice, changing the node controller of a node multiple times is not
* supported behavior.
*/
- (void)__setNodeController:(ASNodeController *)controller;
/**
* Called whenever the node needs to layout its subnodes and, if it's already loaded, its subviews. Executes the layout pass for the node
*

View File

@ -9,6 +9,7 @@
#pragma once
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDimension.h>
@class ASLayout;

View File

@ -15,7 +15,7 @@
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASTextKitComponents.h>
#import <AsyncDisplayKit/ASTextNodeWordKerner.h>
#import "ASTextNodeWordKerner.h"
#import <AsyncDisplayKit/ASThread.h>
@implementation ASEditableTextNodeTargetForAction

View File

@ -7,6 +7,7 @@
//
#import <AsyncDisplayKit/ASExperimentalFeatures.h>
#import <AsyncDisplayKit/ASCollections.h>
NSArray<NSString *> *ASExperimentalFeaturesGetNames(ASExperimentalFeatures flags)

View File

@ -7,10 +7,10 @@
//
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/ASCGImageBuffer.h>
#import "ASCGImageBuffer.h"
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASConfigurationInternal.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import "ASInternalHelpers.h"
#import <UIKit/UIGraphics.h>
#import <UIKit/UIImage.h>
#import <objc/runtime.h>

View File

@ -0,0 +1,31 @@
//
// ASImageNode+CGExtras.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>
/**
@abstract Decides how to scale and crop an image to fit in the provided size, while not wasting memory by upscaling images
@param sourceImageSize The size of the encoded image.
@param boundsSize The bounds in which the image will be displayed.
@param contentMode The mode that defines how image will be scaled and cropped to fit. Supported values are UIViewContentModeScaleToAspectFill and UIViewContentModeScaleToAspectFit.
@param cropRect A rectangle that is to be featured by the cropped image. The rectangle is specified as a "unit rectangle," using fractions of the source image's width and height, e.g. CGRectMake(0.5, 0, 0.5, 1.0) will feature the full right half a photo. If the cropRect is empty, the contentMode will be used to determine the drawRect's size, and only the cropRect's origin will be used for positioning.
@param forceUpscaling A boolean that indicates you would *not* like the backing size to be downscaled if the image is smaller than the destination size. Setting this to YES will result in higher memory usage when images are smaller than their destination.
@param forcedSize A CGSize, that if non-CGSizeZero, indicates that the backing size should be forcedSize and not calculated based on boundsSize.
@discussion If the image is smaller than the size and UIViewContentModeScaleToAspectFill is specified, we suggest the input size so it will be efficiently upscaled on the GPU by the displaying layer at composite time.
*/
AS_EXTERN void ASCroppedImageBackingSizeAndDrawRectInBounds(CGSize sourceImageSize,
CGSize boundsSize,
UIViewContentMode contentMode,
CGRect cropRect,
BOOL forceUpscaling,
CGSize forcedSize,
CGSize *outBackingSize,
CGRect *outDrawRect
);

View File

@ -0,0 +1,123 @@
//
// ASImageNode+CGExtras.mm
// 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 "ASImageNode+CGExtras.h"
#import <cmath>
// TODO rewrite these to be closer to the intended use -- take UIViewContentMode as param, CGRect destinationBounds, CGSize sourceSize.
static CGSize _ASSizeFillWithAspectRatio(CGFloat aspectRatio, CGSize constraints);
static CGSize _ASSizeFitWithAspectRatio(CGFloat aspectRatio, CGSize constraints);
static CGSize _ASSizeFillWithAspectRatio(CGFloat sizeToScaleAspectRatio, CGSize destinationSize)
{
CGFloat destinationAspectRatio = destinationSize.width / destinationSize.height;
if (sizeToScaleAspectRatio > destinationAspectRatio) {
return CGSizeMake(destinationSize.height * sizeToScaleAspectRatio, destinationSize.height);
} else {
return CGSizeMake(destinationSize.width, round(destinationSize.width / sizeToScaleAspectRatio));
}
}
static CGSize _ASSizeFitWithAspectRatio(CGFloat aspectRatio, CGSize constraints)
{
CGFloat constraintAspectRatio = constraints.width / constraints.height;
if (aspectRatio > constraintAspectRatio) {
return CGSizeMake(constraints.width, constraints.width / aspectRatio);
} else {
return CGSizeMake(constraints.height * aspectRatio, constraints.height);
}
}
void ASCroppedImageBackingSizeAndDrawRectInBounds(CGSize sourceImageSize,
CGSize boundsSize,
UIViewContentMode contentMode,
CGRect cropRect,
BOOL forceUpscaling,
CGSize forcedSize,
CGSize *outBackingSize,
CGRect *outDrawRect
)
{
size_t destinationWidth = boundsSize.width;
size_t destinationHeight = boundsSize.height;
// Often, an image is too low resolution to completely fill the width and height provided.
// Per the API contract as commented in the header, we will adjust input parameters (destinationWidth, destinationHeight) to ensure that the image is not upscaled on the CPU.
CGFloat boundsAspectRatio = (CGFloat)destinationWidth / (CGFloat)destinationHeight;
CGSize scaledSizeForImage = sourceImageSize;
BOOL cropToRectDimensions = !CGRectIsEmpty(cropRect);
if (cropToRectDimensions) {
scaledSizeForImage = CGSizeMake(boundsSize.width / cropRect.size.width, boundsSize.height / cropRect.size.height);
} else {
if (contentMode == UIViewContentModeScaleAspectFill)
scaledSizeForImage = _ASSizeFillWithAspectRatio(boundsAspectRatio, sourceImageSize);
else if (contentMode == UIViewContentModeScaleAspectFit)
scaledSizeForImage = _ASSizeFitWithAspectRatio(boundsAspectRatio, sourceImageSize);
}
// If fitting the desired aspect ratio to the image size actually results in a larger buffer, use the input values.
// However, if there is a pixel savings (e.g. we would have to upscale the image), override the function arguments.
if (CGSizeEqualToSize(CGSizeZero, forcedSize) == NO) {
destinationWidth = (size_t)round(forcedSize.width);
destinationHeight = (size_t)round(forcedSize.height);
} else if (forceUpscaling == NO && (scaledSizeForImage.width * scaledSizeForImage.height) < (destinationWidth * destinationHeight)) {
destinationWidth = (size_t)round(scaledSizeForImage.width);
destinationHeight = (size_t)round(scaledSizeForImage.height);
if (destinationWidth == 0 || destinationHeight == 0) {
*outBackingSize = CGSizeZero;
*outDrawRect = CGRectZero;
return;
}
}
// Figure out the scaled size within the destination bounds.
CGFloat sourceImageAspectRatio = sourceImageSize.width / sourceImageSize.height;
CGSize scaledSizeForDestination = CGSizeMake(destinationWidth, destinationHeight);
if (cropToRectDimensions) {
scaledSizeForDestination = CGSizeMake(boundsSize.width / cropRect.size.width, boundsSize.height / cropRect.size.height);
} else {
if (contentMode == UIViewContentModeScaleAspectFill)
scaledSizeForDestination = _ASSizeFillWithAspectRatio(sourceImageAspectRatio, scaledSizeForDestination);
else if (contentMode == UIViewContentModeScaleAspectFit)
scaledSizeForDestination = _ASSizeFitWithAspectRatio(sourceImageAspectRatio, scaledSizeForDestination);
}
// Figure out the rectangle into which to draw the image.
CGRect drawRect = CGRectZero;
if (cropToRectDimensions) {
drawRect = CGRectMake(-cropRect.origin.x * scaledSizeForDestination.width,
-cropRect.origin.y * scaledSizeForDestination.height,
scaledSizeForDestination.width,
scaledSizeForDestination.height);
} else {
// We want to obey the origin of cropRect in aspect-fill mode.
if (contentMode == UIViewContentModeScaleAspectFill) {
drawRect = CGRectMake(((destinationWidth - scaledSizeForDestination.width) * cropRect.origin.x),
((destinationHeight - scaledSizeForDestination.height) * cropRect.origin.y),
scaledSizeForDestination.width,
scaledSizeForDestination.height);
}
// And otherwise just center it.
else {
drawRect = CGRectMake(((destinationWidth - scaledSizeForDestination.width) / 2.0),
((destinationHeight - scaledSizeForDestination.height) / 2.0),
scaledSizeForDestination.width,
scaledSizeForDestination.height);
}
}
*outDrawRect = drawRect;
*outBackingSize = CGSizeMake(destinationWidth, destinationHeight);
}

View File

@ -1,5 +1,5 @@
//
// ASImageContainerProtocolCategories.h
// ASImageNode+Private.h
// Texture
//
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
@ -7,13 +7,11 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASImageProtocols.h>
#pragma once
@interface UIImage (ASImageContainerProtocol) <ASImageContainerProtocol>
@end
@interface NSData (ASImageContainerProtocol) <ASImageContainerProtocol>
@interface ASImageNode (Private)
- (void)_locked_setImage:(UIImage *)image;
- (UIImage *)_locked_Image;
@end

View File

@ -0,0 +1,744 @@
//
// ASImageNode.mm
// 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/ASImageNode.h>
#import <tgmath.h>
#import <AsyncDisplayKit/_ASDisplayLayer.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASDimension.h>
#import "ASDisplayNode+FrameworkPrivate.h"
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASGraphicsContext.h>
#import <AsyncDisplayKit/ASLayout.h>
#import "ASImageNode+CGExtras.h"
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASHashing.h>
#import "ASWeakMap.h"
#import <AsyncDisplayKit/CoreGraphics+ASConvenience.h>
#import "_ASCoreAnimationExtras.h"
// TODO: It would be nice to remove this dependency; it's the only subclass using more than +FrameworkSubclasses.h
#import "ASDisplayNodeInternal.h"
static const CGSize kMinReleaseImageOnBackgroundSize = {20.0, 20.0};
typedef void (^ASImageNodeDrawParametersBlock)(ASWeakMapEntry *entry);
@interface ASImageNodeDrawParameters : NSObject {
@package
UIImage *_image;
BOOL _opaque;
CGRect _bounds;
CGFloat _contentsScale;
UIColor *_backgroundColor;
UIViewContentMode _contentMode;
BOOL _cropEnabled;
BOOL _forceUpscaling;
CGSize _forcedSize;
CGRect _cropRect;
CGRect _cropDisplayBounds;
asimagenode_modification_block_t _imageModificationBlock;
ASDisplayNodeContextModifier _willDisplayNodeContentWithRenderingContext;
ASDisplayNodeContextModifier _didDisplayNodeContentWithRenderingContext;
ASImageNodeDrawParametersBlock _didDrawBlock;
}
@end
@implementation ASImageNodeDrawParameters
@end
/**
* Contains all data that is needed to generate the content bitmap.
*/
@interface ASImageNodeContentsKey : NSObject
@property (nonatomic) UIImage *image;
@property CGSize backingSize;
@property CGRect imageDrawRect;
@property BOOL isOpaque;
@property (nonatomic, copy) UIColor *backgroundColor;
@property (nonatomic) ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext;
@property (nonatomic) ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext;
@property (nonatomic) asimagenode_modification_block_t imageModificationBlock;
@end
@implementation ASImageNodeContentsKey
- (BOOL)isEqual:(id)object
{
if (self == object) {
return YES;
}
// Optimization opportunity: The `isKindOfClass` call here could be avoided by not using the NSObject `isEqual:`
// convention and instead using a custom comparison function that assumes all items are heterogeneous.
// However, profiling shows that our entire `isKindOfClass` expression is only ~1/40th of the total
// overheard of our caching, so it's likely not high-impact.
if ([object isKindOfClass:[ASImageNodeContentsKey class]]) {
ASImageNodeContentsKey *other = (ASImageNodeContentsKey *)object;
return [_image isEqual:other.image]
&& CGSizeEqualToSize(_backingSize, other.backingSize)
&& CGRectEqualToRect(_imageDrawRect, other.imageDrawRect)
&& _isOpaque == other.isOpaque
&& [_backgroundColor isEqual:other.backgroundColor]
&& _willDisplayNodeContentWithRenderingContext == other.willDisplayNodeContentWithRenderingContext
&& _didDisplayNodeContentWithRenderingContext == other.didDisplayNodeContentWithRenderingContext
&& _imageModificationBlock == other.imageModificationBlock;
} else {
return NO;
}
}
- (NSUInteger)hash
{
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
struct {
NSUInteger imageHash;
CGSize backingSize;
CGRect imageDrawRect;
NSInteger isOpaque;
NSUInteger backgroundColorHash;
void *willDisplayNodeContentWithRenderingContext;
void *didDisplayNodeContentWithRenderingContext;
void *imageModificationBlock;
#pragma clang diagnostic pop
} data = {
_image.hash,
_backingSize,
_imageDrawRect,
_isOpaque,
_backgroundColor.hash,
(void *)_willDisplayNodeContentWithRenderingContext,
(void *)_didDisplayNodeContentWithRenderingContext,
(void *)_imageModificationBlock
};
return ASHashBytes(&data, sizeof(data));
}
@end
@implementation ASImageNode
{
@private
UIImage *_image;
ASWeakMapEntry *_weakCacheEntry; // Holds a reference that keeps our contents in cache.
UIColor *_placeholderColor;
void (^_displayCompletionBlock)(BOOL canceled);
// Cropping.
BOOL _cropEnabled; // Defaults to YES.
BOOL _forceUpscaling; //Defaults to NO.
CGSize _forcedSize; //Defaults to CGSizeZero, indicating no forced size.
CGRect _cropRect; // Defaults to CGRectMake(0.5, 0.5, 0, 0)
CGRect _cropDisplayBounds; // Defaults to CGRectNull
}
@synthesize image = _image;
@synthesize imageModificationBlock = _imageModificationBlock;
#pragma mark - Lifecycle
- (instancetype)init
{
if (!(self = [super init]))
return nil;
// TODO can this be removed?
self.contentsScale = ASScreenScale();
self.contentMode = UIViewContentModeScaleAspectFill;
self.opaque = NO;
self.clipsToBounds = YES;
// If no backgroundColor is set to the image node and it's a subview of UITableViewCell, UITableView is setting
// the opaque value of all subviews to YES if highlighting / selection is happening and does not set it back to the
// initial value. With setting a explicit backgroundColor we can prevent that change.
self.backgroundColor = [UIColor clearColor];
_cropEnabled = YES;
_forceUpscaling = NO;
_cropRect = CGRectMake(0.5, 0.5, 0, 0);
_cropDisplayBounds = CGRectNull;
_placeholderColor = ASDisplayNodeDefaultPlaceholderColor();
#ifndef MINIMAL_ASDK
_animatedImageRunLoopMode = ASAnimatedImageDefaultRunLoopMode;
#endif
return self;
}
- (void)dealloc
{
// Invalidate all components around animated images
#ifndef MINIMAL_ASDK
[self invalidateAnimatedImage];
#endif
}
#pragma mark - Placeholder
- (UIImage *)placeholderImage
{
// FIXME: Replace this implementation with reusable CALayers that have .backgroundColor set.
// This would completely eliminate the memory and performance cost of the backing store.
CGSize size = self.calculatedSize;
if ((size.width * size.height) < CGFLOAT_EPSILON) {
return nil;
}
AS::MutexLocker l(__instanceLock__);
ASGraphicsBeginImageContextWithOptions(size, NO, 1);
[self.placeholderColor setFill];
UIRectFill(CGRectMake(0, 0, size.width, size.height));
UIImage *image = ASGraphicsGetImageAndEndCurrentContext();
return image;
}
#pragma mark - Layout and Sizing
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
const auto image = ASLockedSelf(_image);
if (image == nil) {
return [super calculateSizeThatFits:constrainedSize];
}
return image.size;
}
#pragma mark - Setter / Getter
- (void)setImage:(UIImage *)image
{
AS::MutexLocker l(__instanceLock__);
[self _locked_setImage:image];
}
- (void)_locked_setImage:(UIImage *)image
{
ASAssertLocked(__instanceLock__);
if (ASObjectIsEqual(_image, image)) {
return;
}
UIImage *oldImage = _image;
_image = image;
if (image != nil) {
// We explicitly call setNeedsDisplay in this case, although we know setNeedsDisplay will be called with lock held.
// Therefore we have to be careful in methods that are involved with setNeedsDisplay to not run into a deadlock
[self setNeedsDisplay];
if (_displayWithoutProcessing && ASDisplayNodeThreadIsMain()) {
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
if (stretchable) {
ASDisplayNodeSetResizableContents(self, image);
} else {
self.contents = (id)image.CGImage;
}
return;
}
} else {
self.contents = nil;
}
// Destruction of bigger images on the main thread can be expensive
// and can take some time, so we dispatch onto a bg queue to
// actually dealloc.
CGSize oldImageSize = oldImage.size;
BOOL shouldReleaseImageOnBackgroundThread = oldImageSize.width > kMinReleaseImageOnBackgroundSize.width
|| oldImageSize.height > kMinReleaseImageOnBackgroundSize.height;
if (shouldReleaseImageOnBackgroundThread) {
ASPerformBackgroundDeallocation(&oldImage);
}
}
- (UIImage *)image
{
return ASLockedSelf(_image);
}
- (UIColor *)placeholderColor
{
return ASLockedSelf(_placeholderColor);
}
- (void)setPlaceholderColor:(UIColor *)placeholderColor
{
ASLockScopeSelf();
if (ASCompareAssignCopy(_placeholderColor, placeholderColor)) {
_placeholderEnabled = (placeholderColor != nil);
}
}
#pragma mark - Drawing
- (NSObject *)drawParametersForAsyncLayer:(_ASDisplayLayer *)layer
{
ASLockScopeSelf();
ASImageNodeDrawParameters *drawParameters = [[ASImageNodeDrawParameters alloc] init];
drawParameters->_image = _image;
drawParameters->_bounds = [self threadSafeBounds];
drawParameters->_opaque = self.opaque;
drawParameters->_contentsScale = _contentsScaleForDisplay;
drawParameters->_backgroundColor = self.backgroundColor;
drawParameters->_contentMode = self.contentMode;
drawParameters->_cropEnabled = _cropEnabled;
drawParameters->_forceUpscaling = _forceUpscaling;
drawParameters->_forcedSize = _forcedSize;
drawParameters->_cropRect = _cropRect;
drawParameters->_cropDisplayBounds = _cropDisplayBounds;
drawParameters->_imageModificationBlock = _imageModificationBlock;
drawParameters->_willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext;
drawParameters->_didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext;
// Hack for now to retain the weak entry that was created while this drawing happened
drawParameters->_didDrawBlock = ^(ASWeakMapEntry *entry){
ASLockScopeSelf();
_weakCacheEntry = entry;
};
return drawParameters;
}
+ (UIImage *)displayWithParameters:(id<NSObject>)parameter isCancelled:(NS_NOESCAPE asdisplaynode_iscancelled_block_t)isCancelled
{
ASImageNodeDrawParameters *drawParameter = (ASImageNodeDrawParameters *)parameter;
UIImage *image = drawParameter->_image;
if (image == nil) {
return nil;
}
if (true) {
return image;
}
CGRect drawParameterBounds = drawParameter->_bounds;
BOOL forceUpscaling = drawParameter->_forceUpscaling;
CGSize forcedSize = drawParameter->_forcedSize;
BOOL cropEnabled = drawParameter->_cropEnabled;
BOOL isOpaque = drawParameter->_opaque;
UIColor *backgroundColor = drawParameter->_backgroundColor;
UIViewContentMode contentMode = drawParameter->_contentMode;
CGFloat contentsScale = drawParameter->_contentsScale;
CGRect cropDisplayBounds = drawParameter->_cropDisplayBounds;
CGRect cropRect = drawParameter->_cropRect;
asimagenode_modification_block_t imageModificationBlock = drawParameter->_imageModificationBlock;
ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = drawParameter->_willDisplayNodeContentWithRenderingContext;
ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = drawParameter->_didDisplayNodeContentWithRenderingContext;
BOOL hasValidCropBounds = cropEnabled && !CGRectIsEmpty(cropDisplayBounds);
CGRect bounds = (hasValidCropBounds ? cropDisplayBounds : drawParameterBounds);
ASDisplayNodeAssert(contentsScale > 0, @"invalid contentsScale at display time");
// if the image is resizable, bail early since the image has likely already been configured
BOOL stretchable = !UIEdgeInsetsEqualToEdgeInsets(image.capInsets, UIEdgeInsetsZero);
if (stretchable) {
if (imageModificationBlock != NULL) {
image = imageModificationBlock(image);
}
return image;
}
CGSize imageSize = image.size;
CGSize imageSizeInPixels = CGSizeMake(imageSize.width * image.scale, imageSize.height * image.scale);
CGSize boundsSizeInPixels = CGSizeMake(std::floor(bounds.size.width * contentsScale), std::floor(bounds.size.height * contentsScale));
BOOL contentModeSupported = contentMode == UIViewContentModeScaleAspectFill ||
contentMode == UIViewContentModeScaleAspectFit ||
contentMode == UIViewContentModeCenter;
CGSize backingSize = CGSizeZero;
CGRect imageDrawRect = CGRectZero;
if (boundsSizeInPixels.width * contentsScale < 1.0f || boundsSizeInPixels.height * contentsScale < 1.0f ||
imageSizeInPixels.width < 1.0f || imageSizeInPixels.height < 1.0f) {
return nil;
}
// If we're not supposed to do any cropping, just decode image at original size
if (!cropEnabled || !contentModeSupported || stretchable) {
backingSize = imageSizeInPixels;
imageDrawRect = (CGRect){.size = backingSize};
} else {
if (CGSizeEqualToSize(CGSizeZero, forcedSize) == NO) {
//scale forced size
forcedSize.width *= contentsScale;
forcedSize.height *= contentsScale;
}
ASCroppedImageBackingSizeAndDrawRectInBounds(imageSizeInPixels,
boundsSizeInPixels,
contentMode,
cropRect,
forceUpscaling,
forcedSize,
&backingSize,
&imageDrawRect);
}
if (backingSize.width <= 0.0f || backingSize.height <= 0.0f ||
imageDrawRect.size.width <= 0.0f || imageDrawRect.size.height <= 0.0f) {
return nil;
}
ASImageNodeContentsKey *contentsKey = [[ASImageNodeContentsKey alloc] init];
contentsKey.image = image;
contentsKey.backingSize = backingSize;
contentsKey.imageDrawRect = imageDrawRect;
contentsKey.isOpaque = isOpaque;
contentsKey.backgroundColor = backgroundColor;
contentsKey.willDisplayNodeContentWithRenderingContext = willDisplayNodeContentWithRenderingContext;
contentsKey.didDisplayNodeContentWithRenderingContext = didDisplayNodeContentWithRenderingContext;
contentsKey.imageModificationBlock = imageModificationBlock;
if (isCancelled()) {
return nil;
}
ASWeakMapEntry<UIImage *> *entry = [self.class contentsForkey:contentsKey
drawParameters:parameter
isCancelled:isCancelled];
// If nil, we were cancelled.
if (entry == nil) {
return nil;
}
if (drawParameter->_didDrawBlock) {
drawParameter->_didDrawBlock(entry);
}
return entry.value;
}
static ASWeakMap<ASImageNodeContentsKey *, UIImage *> *cache = nil;
+ (ASWeakMapEntry *)contentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
{
static dispatch_once_t onceToken;
static AS::Mutex *cacheLock = nil;
dispatch_once(&onceToken, ^{
cacheLock = new AS::Mutex();
});
{
AS::MutexLocker l(*cacheLock);
if (!cache) {
cache = [[ASWeakMap alloc] init];
}
ASWeakMapEntry *entry = [cache entryForKey:key];
if (entry != nil) {
return entry;
}
}
// cache miss
UIImage *contents = [self createContentsForkey:key drawParameters:drawParameters isCancelled:isCancelled];
if (contents == nil) { // If nil, we were cancelled
return nil;
}
{
AS::MutexLocker l(*cacheLock);
return [cache setObject:contents forKey:key];
}
}
+ (UIImage *)createContentsForkey:(ASImageNodeContentsKey *)key drawParameters:(id)drawParameters isCancelled:(asdisplaynode_iscancelled_block_t)isCancelled
{
// The following `ASGraphicsBeginImageContextWithOptions` call will sometimes take take longer than 5ms on an
// A5 processor for a 400x800 backingSize.
// Check for cancellation before we call it.
if (isCancelled()) {
return nil;
}
// Use contentsScale of 1.0 and do the contentsScale handling in boundsSizeInPixels so ASCroppedImageBackingSizeAndDrawRectInBounds
// will do its rounding on pixel instead of point boundaries
ASGraphicsBeginImageContextWithOptions(key.backingSize, key.isOpaque, 1.0);
BOOL contextIsClean = YES;
CGContextRef context = UIGraphicsGetCurrentContext();
if (context && key.willDisplayNodeContentWithRenderingContext) {
key.willDisplayNodeContentWithRenderingContext(context, drawParameters);
contextIsClean = NO;
}
// if view is opaque, fill the context with background color
if (key.isOpaque && key.backgroundColor) {
[key.backgroundColor setFill];
UIRectFill({ .size = key.backingSize });
contextIsClean = NO;
}
// iOS 9 appears to contain a thread safety regression when drawing the same CGImageRef on
// multiple threads concurrently. In fact, instead of crashing, it appears to deadlock.
// The issue is present in Mac OS X El Capitan and has been seen hanging Pro apps like Adobe Premiere,
// as well as iOS games, and a small number of ASDK apps that provide the same image reference
// to many separate ASImageNodes. A workaround is to set .displaysAsynchronously = NO for the nodes
// that may get the same pointer for a given UI asset image, etc.
// FIXME: We should replace @synchronized here, probably using a global, locked NSMutableSet, and
// only if the object already exists in the set we should create a semaphore to signal waiting threads
// upon removal of the object from the set when the operation completes.
// Another option is to have ASDisplayNode+AsyncDisplay coordinate these cases, and share the decoded buffer.
// Details tracked in https://github.com/facebook/AsyncDisplayKit/issues/1068
UIImage *image = key.image;
BOOL canUseCopy = (contextIsClean || ASImageAlphaInfoIsOpaque(CGImageGetAlphaInfo(image.CGImage)));
CGBlendMode blendMode = canUseCopy ? kCGBlendModeCopy : kCGBlendModeNormal;
@synchronized(image) {
[image drawInRect:key.imageDrawRect blendMode:blendMode alpha:1];
}
if (context && key.didDisplayNodeContentWithRenderingContext) {
key.didDisplayNodeContentWithRenderingContext(context, drawParameters);
}
// Check cancellation one last time before forming image.
if (isCancelled()) {
ASGraphicsEndImageContext();
return nil;
}
UIImage *result = ASGraphicsGetImageAndEndCurrentContext();
if (key.imageModificationBlock) {
result = key.imageModificationBlock(result);
}
return result;
}
- (void)displayDidFinish
{
[super displayDidFinish];
__instanceLock__.lock();
UIImage *image = _image;
void (^displayCompletionBlock)(BOOL canceled) = _displayCompletionBlock;
BOOL shouldPerformDisplayCompletionBlock = (image && displayCompletionBlock);
// Clear the ivar now. The block is retained and will be executed shortly.
if (shouldPerformDisplayCompletionBlock) {
_displayCompletionBlock = nil;
}
__instanceLock__.unlock();
// If we've got a block to perform after displaying, do it.
if (shouldPerformDisplayCompletionBlock) {
displayCompletionBlock(NO);
}
}
- (void)setNeedsDisplayWithCompletion:(void (^ _Nullable)(BOOL canceled))displayCompletionBlock
{
if (self.displaySuspended) {
if (displayCompletionBlock)
displayCompletionBlock(YES);
return;
}
// Stash the block and call-site queue. We'll invoke it in -displayDidFinish.
{
AS::MutexLocker l(__instanceLock__);
if (_displayCompletionBlock != displayCompletionBlock) {
_displayCompletionBlock = displayCompletionBlock;
}
}
[self setNeedsDisplay];
}
#pragma mark Interface State
- (void)clearContents
{
[super clearContents];
AS::MutexLocker l(__instanceLock__);
_weakCacheEntry = nil; // release contents from the cache.
}
#pragma mark - Cropping
- (BOOL)isCropEnabled
{
AS::MutexLocker l(__instanceLock__);
return _cropEnabled;
}
- (void)setCropEnabled:(BOOL)cropEnabled
{
[self setCropEnabled:cropEnabled recropImmediately:NO inBounds:self.bounds];
}
- (void)setCropEnabled:(BOOL)cropEnabled recropImmediately:(BOOL)recropImmediately inBounds:(CGRect)cropBounds
{
__instanceLock__.lock();
if (_cropEnabled == cropEnabled) {
__instanceLock__.unlock();
return;
}
_cropEnabled = cropEnabled;
_cropDisplayBounds = cropBounds;
UIImage *image = _image;
__instanceLock__.unlock();
// If we have an image to display, display it, respecting our recrop flag.
if (image != nil) {
ASPerformBlockOnMainThread(^{
if (recropImmediately)
[self displayImmediately];
else
[self setNeedsDisplay];
});
}
}
- (CGRect)cropRect
{
AS::MutexLocker l(__instanceLock__);
return _cropRect;
}
- (void)setCropRect:(CGRect)cropRect
{
{
AS::MutexLocker l(__instanceLock__);
if (CGRectEqualToRect(_cropRect, cropRect)) {
return;
}
_cropRect = cropRect;
}
// TODO: this logic needs to be updated to respect cropRect.
CGSize boundsSize = self.bounds.size;
CGSize imageSize = self.image.size;
BOOL isCroppingImage = ((boundsSize.width < imageSize.width) || (boundsSize.height < imageSize.height));
// Re-display if we need to.
ASPerformBlockOnMainThread(^{
if (self.nodeLoaded && self.contentMode == UIViewContentModeScaleAspectFill && isCroppingImage)
[self setNeedsDisplay];
});
}
- (BOOL)forceUpscaling
{
AS::MutexLocker l(__instanceLock__);
return _forceUpscaling;
}
- (void)setForceUpscaling:(BOOL)forceUpscaling
{
AS::MutexLocker l(__instanceLock__);
_forceUpscaling = forceUpscaling;
}
- (CGSize)forcedSize
{
AS::MutexLocker l(__instanceLock__);
return _forcedSize;
}
- (void)setForcedSize:(CGSize)forcedSize
{
AS::MutexLocker l(__instanceLock__);
_forcedSize = forcedSize;
}
- (asimagenode_modification_block_t)imageModificationBlock
{
AS::MutexLocker l(__instanceLock__);
return _imageModificationBlock;
}
- (void)setImageModificationBlock:(asimagenode_modification_block_t)imageModificationBlock
{
AS::MutexLocker l(__instanceLock__);
_imageModificationBlock = imageModificationBlock;
}
#pragma mark - Debug
- (void)layout
{
[super layout];
}
@end
#pragma mark - Extras
asimagenode_modification_block_t ASImageNodeRoundBorderModificationBlock(CGFloat borderWidth, UIColor *borderColor)
{
return ^(UIImage *originalImage) {
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
UIBezierPath *roundOutline = [UIBezierPath bezierPathWithOvalInRect:(CGRect){CGPointZero, originalImage.size}];
// Make the image round
[roundOutline addClip];
// Draw the original image
[originalImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
// Draw a border on top.
if (borderWidth > 0.0) {
[borderColor setStroke];
[roundOutline setLineWidth:borderWidth];
[roundOutline stroke];
}
return ASGraphicsGetImageAndEndCurrentContext();
};
}
asimagenode_modification_block_t ASImageNodeTintColorModificationBlock(UIColor *color)
{
return ^(UIImage *originalImage) {
ASGraphicsBeginImageContextWithOptions(originalImage.size, NO, originalImage.scale);
// Set color and render template
[color setFill];
UIImage *templateImage = [originalImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[templateImage drawAtPoint:CGPointZero blendMode:kCGBlendModeCopy alpha:1];
UIImage *modifiedImage = ASGraphicsGetImageAndEndCurrentContext();
// if the original image was stretchy, keep it stretchy
if (!UIEdgeInsetsEqualToEdgeInsets(originalImage.capInsets, UIEdgeInsetsZero)) {
modifiedImage = [modifiedImage resizableImageWithCapInsets:originalImage.capInsets resizingMode:originalImage.resizingMode];
}
return modifiedImage;
};
}

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import "ASInternalHelpers.h"
#import <UIKit/UIKit.h>

View File

@ -12,14 +12,14 @@
#import <atomic>
#import <queue>
#import <AsyncDisplayKit/ASCollections.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASLayoutSpecUtilities.h>
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import "ASLayoutSpecUtilities.h"
#import "ASLayoutSpec+Subclasses.h"
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASCollections.h>
NSString *const ASThreadDictMaxConstraintSizeKey = @"kASThreadDictMaxConstraintSizeKey";
@ -94,7 +94,7 @@ static std::atomic_bool static_retainsSublayoutLayoutElements = ATOMIC_VAR_INIT(
_layoutElementType = layoutElement.layoutElementType;
if (!ASIsCGSizeValidForSize(size)) {
ASDisplayNodeFailAssert(@"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutElement);
//ASDisplayNodeFailAssert(@"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutElement);
size = CGSizeZero;
} else {
size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import "ASDisplayNode+FrameworkPrivate.h"
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASLayoutElement.h>

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASLayoutManager.h>
#import "ASLayoutManager.h"
@implementation ASLayoutManager

View File

@ -7,10 +7,10 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import "ASLayoutSpec+Subclasses.h"
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutSpecPrivate.h>
#import "ASLayoutSpecPrivate.h"
#pragma mark - ASNullLayoutSpec

View File

@ -8,12 +8,12 @@
//
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutSpecPrivate.h>
#import "ASLayoutSpecPrivate.h"
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import "ASLayoutSpec+Subclasses.h"
#import <AsyncDisplayKit/ASCollections.h>
#import <AsyncDisplayKit/ASLayoutElementStylePrivate.h>
#import "ASLayoutElementStylePrivate.h"
#import <AsyncDisplayKit/ASTraitCollection.h>
#import <AsyncDisplayKit/ASEqualityHelpers.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
@ -317,7 +317,17 @@ ASSynthesizeLockingMethodsWithMutex(__instanceLock__)
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName direction:(ASStackLayoutDirection)direction
{
return @"";
NSMutableArray *childStrings = [NSMutableArray array];
for (id<ASLayoutElementAsciiArtProtocol> layoutChild in children) {
NSString *childString = [layoutChild asciiArtString];
if (childString) {
[childStrings addObject:childString];
}
}
if (direction == ASStackLayoutDirectionHorizontal) {
return [ASAsciiArtBoxCreator horizontalBoxStringForChildren:childStrings parent:parentName];
}
return [ASAsciiArtBoxCreator verticalBoxStringForChildren:childStrings parent:parentName];
}
+ (NSString *)asciiArtStringForChildren:(NSArray *)children parentName:(NSString *)parentName

View File

@ -7,9 +7,10 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/_ASTransitionContext.h>
#import <AsyncDisplayKit/ASDisplayNodeLayout.h>
#import "ASDisplayNodeLayout.h"
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNode.h>

View File

@ -7,13 +7,12 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASLayoutTransition.h>
#import "ASLayoutTransition.h"
#import <AsyncDisplayKit/NSArray+Diffing.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h> // Required for _insertSubnode... / _removeFromSupernode.
#import <AsyncDisplayKit/ASLog.h>
#import "ASDisplayNodeInternal.h" // Required for _insertSubnode... / _removeFromSupernode.
#import <queue>
@ -98,7 +97,6 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
[self calculateSubnodeOperationsIfNeeded];
// Create an activity even if no subnodes affected.
as_activity_create_for_scope("Apply subnode insertions and moves");
if (_insertedSubnodePositions.size() == 0 && _subnodeMoves.size() == 0) {
return;
}
@ -131,7 +129,6 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
- (void)applySubnodeRemovals
{
as_activity_scope(as_activity_create("Apply subnode removals", AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT));
MutexLocker l(*__instanceLock__);
[self calculateSubnodeOperationsIfNeeded];
@ -158,7 +155,6 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
}
// Create an activity even if no subnodes affected.
as_activity_create_for_scope("Calculate subnode operations");
ASLayout *previousLayout = _previousLayout.layout;
ASLayout *pendingLayout = _pendingLayout.layout;

View File

@ -7,10 +7,10 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASMainSerialQueue.h>
#import "ASMainSerialQueue.h"
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import "ASInternalHelpers.h"
@interface ASMainSerialQueue ()
{

View File

@ -10,8 +10,7 @@
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
#import <AsyncDisplayKit/ASLog.h>
#import "ASInternalHelpers.h"
#import <AsyncDisplayKit/ASThread.h>
#import <objc/runtime.h>
@ -42,15 +41,12 @@
}
if ([object_getClass(value) needsMainThreadDeallocation]) {
as_log_debug(ASMainThreadDeallocationLog(), "%@: Trampolining ivar '%s' value %@ for main deallocation.", self, ivar_getName(ivar), value);
// Release the ivar's reference before handing the object to the queue so we
// don't risk holding onto it longer than the queue does.
object_setIvar(self, ivar, nil);
ASPerformMainThreadDeallocation(&value);
} else {
as_log_debug(ASMainThreadDeallocationLog(), "%@: Not trampolining ivar '%s' value %@.", self, ivar_getName(ivar), value);
}
}
}
@ -112,16 +108,13 @@
// If it's `id` we have to include it just in case.
resultIvars[resultCount] = ivar;
resultCount += 1;
as_log_verbose(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for possible main deallocation due to type id", self, ivar_getName(ivar));
} else {
// If it's an ivar with a static type, check the type.
Class c = ASGetClassFromType(type);
if ([c needsMainThreadDeallocation]) {
resultIvars[resultCount] = ivar;
resultCount += 1;
as_log_verbose(ASMainThreadDeallocationLog(), "%@: Marking ivar '%s' for main deallocation due to class %@", self, ivar_getName(ivar), c);
} else {
as_log_verbose(ASMainThreadDeallocationLog(), "%@: Skipping ivar '%s' for main deallocation.", self, ivar_getName(ivar));
}
}
}

View File

@ -11,7 +11,7 @@
#import <UIKit/UIGeometry.h>
#import <AsyncDisplayKit/NSIndexSet+ASHelpers.h>
#import "NSIndexSet+ASHelpers.h"
NSString *ASGetDescriptionValueString(id object)
{

View File

@ -7,10 +7,10 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASPendingStateController.h>
#import "ASPendingStateController.h"
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASWeakSet.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h> // Required for -applyPendingViewState; consider moving this to +FrameworkPrivate
#import "ASDisplayNodeInternal.h" // Required for -applyPendingViewState; consider moving this to +FrameworkPrivate
@interface ASPendingStateController()
{

View File

@ -6,7 +6,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASRecursiveUnfairLock.h>
#import "ASRecursiveUnfairLock.h"
#import <stdatomic.h>

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASResponderChainEnumerator.h>
#import "ASResponderChainEnumerator.h"
#import <AsyncDisplayKit/ASAssert.h>
@implementation ASResponderChainEnumerator {

View File

@ -9,11 +9,10 @@
#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASConfigurationInternal.h>
#import <AsyncDisplayKit/ASLog.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASRunLoopQueue.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASSignpost.h>
#import "ASSignpost.h"
#import <QuartzCore/QuartzCore.h>
#import <cstdlib>
#import <deque>
@ -115,9 +114,6 @@ static void runLoopSourceCallback(void *info) {
NSPointerArray *_internalQueue; // Use NSPointerArray so we can decide __strong or __weak per-instance.
AS::RecursiveMutex _internalQueueLock;
// In order to not pollute the top-level activities, each queue has 1 root activity.
os_activity_t _rootActivity;
#if ASRunLoopQueueLoggingEnabled
NSTimer *_runloopQueueLoggingTimer;
#endif
@ -138,15 +134,6 @@ static void runLoopSourceCallback(void *info) {
_queueConsumer = handlerBlock;
_batchSize = 1;
_ensureExclusiveMembership = YES;
// We don't want to pollute the top-level app activities with run loop batches, so we create one top-level
// activity per queue, and each batch activity joins that one instead.
_rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT);
{
// Log a message identifying this queue into the queue's root activity.
as_activity_scope_verbose(_rootActivity);
as_log_verbose(ASDisplayLog(), "Created run loop queue: %@", self);
}
// Self is guaranteed to outlive the observer. Without the high cost of a weak pointer,
// __unsafe_unretained allows us to avoid flagging the memory cycle detector.
@ -260,15 +247,10 @@ static void runLoopSourceCallback(void *info) {
// itemsToProcess will be empty if _queueConsumer == nil so no need to check again.
const auto count = itemsToProcess.size();
if (count > 0) {
as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
const auto itemsEnd = itemsToProcess.cend();
for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) {
__unsafe_unretained id value = *iterator;
_queueConsumer(value, isQueueDrained && iterator == itemsEnd - 1);
as_log_verbose(ASDisplayLog(), "processed %@", value);
}
if (count > 1) {
as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count);
}
}
@ -339,7 +321,6 @@ ASSynthesizeLockingMethodsWithMutex(_internalQueueLock)
AS::Mutex _internalQueueLock;
// In order to not pollute the top-level activities, each queue has 1 root activity.
os_activity_t _rootActivity;
#if ASRunLoopQueueLoggingEnabled
NSTimer *_runloopQueueLoggingTimer;
@ -368,15 +349,6 @@ dispatch_once_t _ASSharedCATransactionQueueOnceToken;
_internalQueue.reserve(kInternalQueueInitialCapacity);
_batchBuffer.reserve(kInternalQueueInitialCapacity);
// We don't want to pollute the top-level app activities with run loop batches, so we create one top-level
// activity per queue, and each batch activity joins that one instead.
_rootActivity = as_activity_create("Process run loop queue items", OS_ACTIVITY_NONE, OS_ACTIVITY_FLAG_DEFAULT);
{
// Log a message identifying this queue into the queue's root activity.
as_activity_scope_verbose(_rootActivity);
as_log_verbose(ASDisplayLog(), "Created run loop queue: %@", self);
}
// Self is guaranteed to outlive the observer. Without the high cost of a weak pointer,
// __unsafe_unretained allows us to avoid flagging the memory cycle detector.
__unsafe_unretained __typeof__(self) weakSelf = self;
@ -439,7 +411,6 @@ dispatch_once_t _ASSharedCATransactionQueueOnceToken;
if (count == 0) {
return;
}
as_activity_scope_verbose(as_activity_create("Process run loop queue batch", _rootActivity, OS_ACTIVITY_FLAG_DEFAULT));
ASSignpostStart(ASSignpostRunLoopQueueBatch);
// Swap buffers, clear our hash table.
@ -451,10 +422,8 @@ dispatch_once_t _ASSharedCATransactionQueueOnceToken;
for (const id<ASCATransactionQueueObserving> &value : _batchBuffer) {
[value prepareForCATransactionCommit];
as_log_verbose(ASDisplayLog(), "processed %@", value);
}
_batchBuffer.clear();
as_log_verbose(ASDisplayLog(), "processed %lu items", (unsigned long)count);
ASSignpostEnd(ASSignpostRunLoopQueueBatch);
}

View File

@ -9,7 +9,7 @@
#import <AsyncDisplayKit/ASScrollNode.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
#import "ASDisplayNode+FrameworkPrivate.h"
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASLayout.h>
@ -87,7 +87,7 @@
restrictedToSize:(ASLayoutElementSize)size
relativeToParentSize:(CGSize)parentSize
{
ASLockScopeSelf();
ASScopedLockSelfOrToRoot();
ASSizeRange contentConstrainedSize = constrainedSize;
if (ASScrollDirectionContainsVerticalDirection(_scrollableDirections)) {

View File

@ -7,11 +7,11 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASTextKitContext.h>
#import "ASTextKitContext.h"
#if AS_ENABLE_TEXTNODE
#import <AsyncDisplayKit/ASLayoutManager.h>
#import "ASLayoutManager.h"
#import <AsyncDisplayKit/ASThread.h>
#include <memory>

View File

@ -0,0 +1,34 @@
//
// ASTextNodeCommon.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/ASAvailability.h>
#define AS_TEXT_ALERT_UNIMPLEMENTED_FEATURE() { \
static dispatch_once_t onceToken; \
dispatch_once(&onceToken, ^{ \
NSLog(@"[Texture] Warning: Feature %@ is unimplemented in %@.", NSStringFromSelector(_cmd), NSStringFromClass(self.class)); \
});\
}
/**
* Highlight styles.
*/
typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
/**
* Highlight style for text on a light background.
*/
ASTextNodeHighlightStyleLight,
/**
* Highlight style for text on a dark background.
*/
ASTextNodeHighlightStyleDark
};

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASTextNodeWordKerner.h>
#import "ASTextNodeWordKerner.h"
#import <UIKit/UIKit.h>

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASWeakMap.h>
#import "ASWeakMap.h"
@interface ASWeakMapEntry ()
@property (nonatomic, readonly) id key;

View File

@ -7,7 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASWeakProxy.h>
#import "ASWeakProxy.h"
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
#import <AsyncDisplayKit/ASAssert.h>

View File

@ -1,33 +0,0 @@
//
// ASAbstractLayoutController.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/ASLayoutController.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
AS_EXTERN ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection, ASRangeTuningParameters rangeTuningParameters);
AS_EXTERN ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection, ASRangeTuningParameters rangeTuningParameters);
AS_EXTERN CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningParameters tuningParameters, ASScrollDirection scrollableDirections, ASScrollDirection scrollDirection);
@interface ASAbstractLayoutController : NSObject <ASLayoutController>
@end
@interface ASAbstractLayoutController (Unavailable)
- (NSHashTable *)indexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType __unavailable;
- (void)allIndexPathsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable * _Nullable * _Nullable)preloadSet __unavailable;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,185 +0,0 @@
//
// ASAbstractLayoutController.mm
// 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/ASAbstractLayoutController.h>
#import <AsyncDisplayKit/ASAbstractLayoutController+FrameworkPrivate.h>
#import <AsyncDisplayKit/ASAssert.h>
ASRangeTuningParameters const ASRangeTuningParametersZero = {};
BOOL ASRangeTuningParametersEqualToRangeTuningParameters(ASRangeTuningParameters lhs, ASRangeTuningParameters rhs)
{
return lhs.leadingBufferScreenfuls == rhs.leadingBufferScreenfuls && lhs.trailingBufferScreenfuls == rhs.trailingBufferScreenfuls;
}
ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferHorizontal(ASScrollDirection scrollDirection,
ASRangeTuningParameters rangeTuningParameters)
{
ASDirectionalScreenfulBuffer horizontalBuffer = {0, 0};
BOOL movingRight = ASScrollDirectionContainsRight(scrollDirection);
horizontalBuffer.positiveDirection = movingRight ? rangeTuningParameters.leadingBufferScreenfuls
: rangeTuningParameters.trailingBufferScreenfuls;
horizontalBuffer.negativeDirection = movingRight ? rangeTuningParameters.trailingBufferScreenfuls
: rangeTuningParameters.leadingBufferScreenfuls;
return horizontalBuffer;
}
ASDirectionalScreenfulBuffer ASDirectionalScreenfulBufferVertical(ASScrollDirection scrollDirection,
ASRangeTuningParameters rangeTuningParameters)
{
ASDirectionalScreenfulBuffer verticalBuffer = {0, 0};
BOOL movingDown = ASScrollDirectionContainsDown(scrollDirection);
verticalBuffer.positiveDirection = movingDown ? rangeTuningParameters.leadingBufferScreenfuls
: rangeTuningParameters.trailingBufferScreenfuls;
verticalBuffer.negativeDirection = movingDown ? rangeTuningParameters.trailingBufferScreenfuls
: rangeTuningParameters.leadingBufferScreenfuls;
return verticalBuffer;
}
CGRect CGRectExpandHorizontally(CGRect rect, ASDirectionalScreenfulBuffer buffer)
{
CGFloat negativeDirectionWidth = buffer.negativeDirection * rect.size.width;
CGFloat positiveDirectionWidth = buffer.positiveDirection * rect.size.width;
rect.size.width = negativeDirectionWidth + rect.size.width + positiveDirectionWidth;
rect.origin.x -= negativeDirectionWidth;
return rect;
}
CGRect CGRectExpandVertically(CGRect rect, ASDirectionalScreenfulBuffer buffer)
{
CGFloat negativeDirectionHeight = buffer.negativeDirection * rect.size.height;
CGFloat positiveDirectionHeight = buffer.positiveDirection * rect.size.height;
rect.size.height = negativeDirectionHeight + rect.size.height + positiveDirectionHeight;
rect.origin.y -= negativeDirectionHeight;
return rect;
}
CGRect CGRectExpandToRangeWithScrollableDirections(CGRect rect, ASRangeTuningParameters tuningParameters,
ASScrollDirection scrollableDirections, ASScrollDirection scrollDirection)
{
// Can scroll horizontally - expand the range appropriately
if (ASScrollDirectionContainsHorizontalDirection(scrollableDirections)) {
ASDirectionalScreenfulBuffer horizontalBuffer = ASDirectionalScreenfulBufferHorizontal(scrollDirection, tuningParameters);
rect = CGRectExpandHorizontally(rect, horizontalBuffer);
}
// Can scroll vertically - expand the range appropriately
if (ASScrollDirectionContainsVerticalDirection(scrollableDirections)) {
ASDirectionalScreenfulBuffer verticalBuffer = ASDirectionalScreenfulBufferVertical(scrollDirection, tuningParameters);
rect = CGRectExpandVertically(rect, verticalBuffer);
}
return rect;
}
@interface ASAbstractLayoutController () {
std::vector<std::vector<ASRangeTuningParameters>> _tuningParameters;
}
@end
@implementation ASAbstractLayoutController
+ (std::vector<std::vector<ASRangeTuningParameters>>)defaultTuningParameters
{
auto tuningParameters = std::vector<std::vector<ASRangeTuningParameters>> (ASLayoutRangeModeCount, std::vector<ASRangeTuningParameters> (ASLayoutRangeTypeCount));
tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 1.0,
.trailingBufferScreenfuls = 0.5
};
tuningParameters[ASLayoutRangeModeFull][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 2.5,
.trailingBufferScreenfuls = 1.5
};
tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0.25,
.trailingBufferScreenfuls = 0.25
};
tuningParameters[ASLayoutRangeModeMinimum][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0.5,
.trailingBufferScreenfuls = 0.25
};
tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
tuningParameters[ASLayoutRangeModeVisibleOnly][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
// The Low Memory range mode has special handling. Because a zero range still includes the visible area / bounds,
// in order to implement the behavior of releasing all graphics memory (backing stores), ASRangeController must check
// for this range mode and use an empty set for displayIndexPaths rather than querying the ASLayoutController for the indexPaths.
tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypeDisplay] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
tuningParameters[ASLayoutRangeModeLowMemory][ASLayoutRangeTypePreload] = {
.leadingBufferScreenfuls = 0,
.trailingBufferScreenfuls = 0
};
return tuningParameters;
}
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
ASDisplayNodeAssert(self.class != [ASAbstractLayoutController class], @"Should never create instances of abstract class ASAbstractLayoutController.");
_tuningParameters = [[self class] defaultTuningParameters];
return self;
}
#pragma mark - Tuning Parameters
- (ASRangeTuningParameters)tuningParametersForRangeType:(ASLayoutRangeType)rangeType
{
return [self tuningParametersForRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeType:(ASLayoutRangeType)rangeType
{
return [self setTuningParameters:tuningParameters forRangeMode:ASLayoutRangeModeFull rangeType:rangeType];
}
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Requesting a range that is OOB for the configured tuning parameters");
return _tuningParameters[rangeMode][rangeType];
}
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType
{
ASDisplayNodeAssert(rangeMode < _tuningParameters.size() && rangeType < _tuningParameters[rangeMode].size(), @"Setting a range that is OOB for the configured tuning parameters");
_tuningParameters[rangeMode][rangeType] = tuningParameters;
}
#pragma mark - Abstract Index Path Range Support
- (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map
{
ASDisplayNodeAssertNotSupported();
return nil;
}
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)displaySet preloadSet:(NSHashTable<ASCollectionElement *> *__autoreleasing _Nullable *)preloadSet map:(ASElementMap *)map
{
ASDisplayNodeAssertNotSupported();
}
@end

View File

@ -1,56 +0,0 @@
//
// ASDelegateProxy.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>
@class ASDelegateProxy;
@protocol ASDelegateProxyInterceptor <NSObject>
@required
// Called if the target object is discovered to be nil if it had been non-nil at init time.
// This happens if the object is deallocated, because the proxy must maintain a weak reference to avoid cycles.
// Though the target object may become nil, the interceptor must not; it is assumed the interceptor owns the proxy.
- (void)proxyTargetHasDeallocated:(ASDelegateProxy *)proxy;
@end
/**
* Stand-in for delegates like UITableView or UICollectionView's delegate / dataSource.
* Any selectors flagged by "interceptsSelector" are routed to the interceptor object and are not delivered to the target.
* Everything else leaves AsyncDisplayKit safely and arrives at the original target object.
*/
@interface ASDelegateProxy : NSProxy
- (instancetype)initWithTarget:(id)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor;
// This method must be overridden by a subclass.
- (BOOL)interceptsSelector:(SEL)selector;
@end
/**
* ASTableView intercepts and/or overrides a few of UITableView's critical data source and delegate methods.
*
* Any selector included in this function *MUST* be implemented by ASTableView.
*/
@interface ASTableViewProxy : ASDelegateProxy
@end
/**
* ASCollectionView intercepts and/or overrides a few of UICollectionView's critical data source and delegate methods.
*
* Any selector included in this function *MUST* be implemented by ASCollectionView.
*/
@interface ASCollectionViewProxy : ASDelegateProxy
@end
@interface ASPagerNodeProxy : ASDelegateProxy
@end

View File

@ -1,273 +0,0 @@
//
// ASDelegateProxy.mm
// 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
#import <AsyncDisplayKit/ASDelegateProxy.h>
#import <AsyncDisplayKit/ASTableNode.h>
#import <AsyncDisplayKit/ASCollectionNode.h>
#import <AsyncDisplayKit/ASAssert.h>
// UIKit performs a class check for UIDataSourceModelAssociation protocol conformance rather than an instance check, so
// the implementation of conformsToProtocol: below never gets called. We need to declare the two as conforming to the protocol here, then
// we need to implement dummy methods to get rid of a compiler warning about not conforming to the protocol.
@interface ASTableViewProxy () <UIDataSourceModelAssociation>
@end
@interface ASCollectionViewProxy () <UIDataSourceModelAssociation>
@end
@interface ASDelegateProxy (UIDataSourceModelAssociationPrivate)
- (nullable NSString *)_modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view;
- (nullable NSIndexPath *)_indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view;
@end
@implementation ASTableViewProxy
- (BOOL)interceptsSelector:(SEL)selector
{
return (
// handled by ASTableView node<->cell machinery
selector == @selector(tableView:cellForRowAtIndexPath:) ||
selector == @selector(tableView:heightForRowAtIndexPath:) ||
// Selection, highlighting, menu
selector == @selector(tableView:willSelectRowAtIndexPath:) ||
selector == @selector(tableView:didSelectRowAtIndexPath:) ||
selector == @selector(tableView:willDeselectRowAtIndexPath:) ||
selector == @selector(tableView:didDeselectRowAtIndexPath:) ||
selector == @selector(tableView:shouldHighlightRowAtIndexPath:) ||
selector == @selector(tableView:didHighlightRowAtIndexPath:) ||
selector == @selector(tableView:didUnhighlightRowAtIndexPath:) ||
selector == @selector(tableView:shouldShowMenuForRowAtIndexPath:) ||
selector == @selector(tableView:canPerformAction:forRowAtIndexPath:withSender:) ||
selector == @selector(tableView:performAction:forRowAtIndexPath:withSender:) ||
// handled by ASRangeController
selector == @selector(numberOfSectionsInTableView:) ||
selector == @selector(tableView:numberOfRowsInSection:) ||
// reordering support
selector == @selector(tableView:canMoveRowAtIndexPath:) ||
selector == @selector(tableView:moveRowAtIndexPath:toIndexPath:) ||
// used for ASCellNode visibility
selector == @selector(scrollViewDidScroll:) ||
// used for ASCellNode user interaction
selector == @selector(scrollViewWillBeginDragging:) ||
selector == @selector(scrollViewDidEndDragging:willDecelerate:) ||
// used for ASRangeController visibility updates
selector == @selector(tableView:willDisplayCell:forRowAtIndexPath:) ||
selector == @selector(tableView:didEndDisplayingCell:forRowAtIndexPath:) ||
// used for batch fetching API
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) ||
selector == @selector(scrollViewDidEndDecelerating:) ||
// UIDataSourceModelAssociation
selector == @selector(modelIdentifierForElementAtIndexPath:inView:) ||
selector == @selector(indexPathForElementWithModelIdentifier:inView:)
);
}
- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view {
return [self _modelIdentifierForElementAtIndexPath:indexPath inView:view];
}
- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
return [self _indexPathForElementWithModelIdentifier:identifier inView:view];
}
@end
@implementation ASCollectionViewProxy
- (BOOL)interceptsSelector:(SEL)selector
{
return (
// handled by ASCollectionView node<->cell machinery
selector == @selector(collectionView:cellForItemAtIndexPath:) ||
selector == @selector(collectionView:layout:sizeForItemAtIndexPath:) ||
selector == @selector(collectionView:layout:insetForSectionAtIndex:) ||
selector == @selector(collectionView:layout:minimumLineSpacingForSectionAtIndex:) ||
selector == @selector(collectionView:layout:minimumInteritemSpacingForSectionAtIndex:) ||
selector == @selector(collectionView:layout:referenceSizeForHeaderInSection:) ||
selector == @selector(collectionView:layout:referenceSizeForFooterInSection:) ||
selector == @selector(collectionView:viewForSupplementaryElementOfKind:atIndexPath:) ||
// Selection, highlighting, menu
selector == @selector(collectionView:shouldSelectItemAtIndexPath:) ||
selector == @selector(collectionView:didSelectItemAtIndexPath:) ||
selector == @selector(collectionView:shouldDeselectItemAtIndexPath:) ||
selector == @selector(collectionView:didDeselectItemAtIndexPath:) ||
selector == @selector(collectionView:shouldHighlightItemAtIndexPath:) ||
selector == @selector(collectionView:didHighlightItemAtIndexPath:) ||
selector == @selector(collectionView:didUnhighlightItemAtIndexPath:) ||
selector == @selector(collectionView:shouldShowMenuForItemAtIndexPath:) ||
selector == @selector(collectionView:canPerformAction:forItemAtIndexPath:withSender:) ||
selector == @selector(collectionView:performAction:forItemAtIndexPath:withSender:) ||
// Item counts
selector == @selector(numberOfSectionsInCollectionView:) ||
selector == @selector(collectionView:numberOfItemsInSection:) ||
// Element appearance callbacks
selector == @selector(collectionView:willDisplayCell:forItemAtIndexPath:) ||
selector == @selector(collectionView:didEndDisplayingCell:forItemAtIndexPath:) ||
selector == @selector(collectionView:willDisplaySupplementaryView:forElementKind:atIndexPath:) ||
selector == @selector(collectionView:didEndDisplayingSupplementaryView:forElementOfKind:atIndexPath:) ||
// used for batch fetching API
selector == @selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:) ||
selector == @selector(scrollViewDidEndDecelerating:) ||
// used for ASCellNode visibility
selector == @selector(scrollViewDidScroll:) ||
// used for ASCellNode user interaction
selector == @selector(scrollViewWillBeginDragging:) ||
selector == @selector(scrollViewDidEndDragging:willDecelerate:) ||
// intercepted due to not being supported by ASCollectionView (prevent bugs caused by usage)
selector == @selector(collectionView:canMoveItemAtIndexPath:) ||
selector == @selector(collectionView:moveItemAtIndexPath:toIndexPath:) ||
// UIDataSourceModelAssociation
selector == @selector(modelIdentifierForElementAtIndexPath:inView:) ||
selector == @selector(indexPathForElementWithModelIdentifier:inView:)
);
}
- (nullable NSString *)modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view {
return [self _modelIdentifierForElementAtIndexPath:indexPath inView:view];
}
- (nullable NSIndexPath *)indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
return [self _indexPathForElementWithModelIdentifier:identifier inView:view];
}
@end
@implementation ASPagerNodeProxy
- (BOOL)interceptsSelector:(SEL)selector
{
return (
// handled by ASPagerDataSource node<->cell machinery
selector == @selector(collectionNode:nodeForItemAtIndexPath:) ||
selector == @selector(collectionNode:nodeBlockForItemAtIndexPath:) ||
selector == @selector(collectionNode:numberOfItemsInSection:) ||
selector == @selector(collectionNode:constrainedSizeForItemAtIndexPath:)
);
}
@end
@implementation ASDelegateProxy {
id <ASDelegateProxyInterceptor> __weak _interceptor;
id __weak _target;
}
- (instancetype)initWithTarget:(id)target interceptor:(id <ASDelegateProxyInterceptor>)interceptor
{
ASDisplayNodeAssert(interceptor, @"interceptor must not be nil");
_target = target ? : [NSNull null];
_interceptor = interceptor;
return self;
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{
id target = _target;
if (target) {
return [target conformsToProtocol:aProtocol];
} else {
return [super conformsToProtocol:aProtocol];
}
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([self interceptsSelector:aSelector]) {
return [_interceptor respondsToSelector:aSelector];
} else {
// Also return NO if _target has become nil due to zeroing weak reference (or placeholder initialization).
return [_target respondsToSelector:aSelector];
}
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
if ([self interceptsSelector:aSelector]) {
return _interceptor;
} else {
id target = _target;
if (target) {
return [target respondsToSelector:aSelector] ? target : nil;
} else {
// The _interceptor needs to be nilled out in this scenario. For that a strong reference needs to be created
// to be able to nil out the _interceptor but still let it know that the proxy target has deallocated
// We have to hold a strong reference to the interceptor as we have to nil it out and call the proxyTargetHasDeallocated
// The reason that the interceptor needs to be nilled out is that there maybe a change of a infinite loop, for example
// if a method will be called in the proxyTargetHasDeallocated: that again would trigger a whole new forwarding cycle
id <ASDelegateProxyInterceptor> interceptor = _interceptor;
_interceptor = nil;
[interceptor proxyTargetHasDeallocated:self];
return nil;
}
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
// Check for a compiled definition for the selector
NSMethodSignature *methodSignature = nil;
if ([self interceptsSelector:aSelector]) {
methodSignature = [[_interceptor class] instanceMethodSignatureForSelector:aSelector];
} else {
methodSignature = [[_target class] instanceMethodSignatureForSelector:aSelector];
}
// Unfortunately, in order to get this object to work properly, the use of a method which creates an NSMethodSignature
// from a C string. -methodSignatureForSelector is called when a compiled definition for the selector cannot be found.
// This is the place where we have to create our own dud NSMethodSignature. This is necessary because if this method
// returns nil, a selector not found exception is raised. The string argument to -signatureWithObjCTypes: outlines
// the return type and arguments to the message. To return a dud NSMethodSignature, pretty much any signature will
// suffice. Since the -forwardInvocation call will do nothing if the delegate does not respond to the selector,
// the dud NSMethodSignature simply gets us around the exception.
return methodSignature ?: [NSMethodSignature signatureWithObjCTypes:"@^v^c"];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
// If we are down here this means _interceptor and _target where nil. Just don't do anything to prevent a crash
}
- (BOOL)interceptsSelector:(SEL)selector
{
ASDisplayNodeAssert(NO, @"This method must be overridden by subclasses.");
return NO;
}
- (nullable NSString *)_modelIdentifierForElementAtIndexPath:(NSIndexPath *)indexPath inView:(UIView *)view {
return [(id)_interceptor modelIdentifierForElementAtIndexPath:indexPath inView:view];
}
- (nullable NSIndexPath *)_indexPathForElementWithModelIdentifier:(NSString *)identifier inView:(UIView *)view {
return [(id)_interceptor indexPathForElementWithModelIdentifier:identifier inView:view];
}
@end
#endif

View File

@ -1,21 +0,0 @@
//
// ASDisplayNode+DebugTiming.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>
@interface ASDisplayNode (DebugTiming)
@property (nonatomic, readonly) NSTimeInterval debugTimeToCreateView;
@property (nonatomic, readonly) NSTimeInterval debugTimeToApplyPendingState;
@property (nonatomic, readonly) NSTimeInterval debugTimeToAddSubnodeViews;
@property (nonatomic, readonly) NSTimeInterval debugTimeForDidLoad;
- (NSTimeInterval)debugAllCreationTime;
@end

View File

@ -1,85 +0,0 @@
//
// ASDisplayNode+DebugTiming.mm
// 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+DebugTiming.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
@implementation ASDisplayNode (DebugTiming)
#if TIME_DISPLAYNODE_OPS
- (NSTimeInterval)debugTimeToCreateView
{
return _debugTimeToCreateView;
}
- (NSTimeInterval)debugTimeToApplyPendingState
{
return _debugTimeToApplyPendingState;
}
- (NSTimeInterval)debugTimeToAddSubnodeViews
{
return _debugTimeToAddSubnodeViews;
}
- (NSTimeInterval)debugTimeForDidLoad
{
return _debugTimeForDidLoad;
}
- (NSTimeInterval)debugAllCreationTime
{
return self.debugTimeToCreateView + self.debugTimeToApplyPendingState + self.debugTimeToAddSubnodeViews + self.debugTimeForDidLoad;
}
// This would over-count views that are created in the parent's didload or addsubnodesubviews, so we need to take a more basic approach
//- (NSTimeInterval)debugRecursiveAllCreationTime
//{
// __block NSTimeInterval total = 0;
// ASDisplayNodeFindAllSubnodes(self, ^(ASDisplayNode *n){
// total += self.debugTimeToCreateView;
// total += self.debugTimeToApplyPendingState;
// total += self.debugTimeToAddSubnodeViews;
// total += self.debugTimeForDidLoad;
// return NO;
// });
// return total;
//}
#else
// These ivars are compiled out so we don't have the info available
- (NSTimeInterval)debugTimeToCreateView
{
return -1;
}
- (NSTimeInterval)debugTimeToApplyPendingState
{
return -1;
}
- (NSTimeInterval)debugTimeToAddSubnodeViews
{
return -1;
}
- (NSTimeInterval)debugTimeForDidLoad
{
return -1;
}
- (NSTimeInterval)debugAllCreationTime
{
return -1;
}
#endif
@end

View File

@ -1,137 +0,0 @@
//
// ASElementMap.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
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCollectionElement, ASSection, UICollectionViewLayoutAttributes;
@protocol ASSectionContext;
/**
* An immutable representation of the state of a collection view's data.
* All items and supplementary elements are represented by ASCollectionElement.
* Fast enumeration is in terms of ASCollectionElement.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASElementMap : NSObject <NSCopying, NSFastEnumeration>
/**
* The total number of elements in this map.
*/
@property (readonly) NSUInteger count;
/**
* The number of sections (of items) in this map.
*/
@property (readonly) NSInteger numberOfSections;
/**
* The kinds of supplementary elements present in this map. O(1)
*/
@property (copy, readonly) NSArray<NSString *> *supplementaryElementKinds;
/**
* Returns number of items in the given section. O(1)
*/
- (NSInteger)numberOfItemsInSection:(NSInteger)section;
/**
* Returns the context object for the given section, if any. O(1)
*/
- (nullable id<ASSectionContext>)contextForSection:(NSInteger)section;
/**
* All the index paths for all the items in this map. O(N)
*
* This property may be removed in the future, since it doesn't account for supplementary nodes.
*/
@property (copy, readonly) NSArray<NSIndexPath *> *itemIndexPaths;
/**
* All the item elements in this map, in ascending order. O(N)
*/
@property (copy, readonly) NSArray<ASCollectionElement *> *itemElements;
/**
* Returns the index path that corresponds to the same element in @c map at the given @c indexPath.
* O(1) for items, fast O(N) for sections.
*
* Note you can pass "section index paths" of length 1 and get a corresponding section index path.
*/
- (nullable NSIndexPath *)convertIndexPath:(NSIndexPath *)indexPath fromMap:(ASElementMap *)map;
/**
* Returns the section index into the receiver that corresponds to the same element in @c map at @c sectionIndex. Fast O(N).
*
* Returns @c NSNotFound if the section does not exist in the receiver.
*/
- (NSInteger)convertSection:(NSInteger)sectionIndex fromMap:(ASElementMap *)map;
/**
* Returns the index path for the given element. O(1)
*/
- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element;
/**
* Returns the index path for the given element, if it represents a cell. O(1)
*/
- (nullable NSIndexPath *)indexPathForElementIfCell:(ASCollectionElement *)element;
/**
* Returns the item-element at the given index path. O(1)
*/
- (nullable ASCollectionElement *)elementForItemAtIndexPath:(NSIndexPath *)indexPath;
/**
* Returns the element for the supplementary element of the given kind at the given index path. O(1)
*/
- (nullable ASCollectionElement *)supplementaryElementOfKind:(NSString *)supplementaryElementKind atIndexPath:(NSIndexPath *)indexPath;
/**
* Returns the element that corresponds to the given layout attributes, if any.
*
* NOTE: This method only regards the category, kind, and index path of the attributes object. Elements do not
* have any concept of size/position.
*/
- (nullable ASCollectionElement *)elementForLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes;
/**
* A very terse description e.g. { itemCounts = [ <S0: 1> <S1: 16> ] }
*/
@property (readonly) NSString *smallDescription;
#pragma mark - Initialization -- Only Useful to ASDataController
// SectionIndex -> ItemIndex -> Element
typedef NSArray<NSArray<ASCollectionElement *> *> ASCollectionElementTwoDimensionalArray;
// ElementKind -> IndexPath -> Element
typedef NSDictionary<NSString *, NSDictionary<NSIndexPath *, ASCollectionElement *> *> ASSupplementaryElementDictionary;
/**
* Create a new element map for this dataset. You probably don't need to use this ASDataController is the only one who creates these.
*
* @param sections The array of ASSection objects.
* @param items A 2D array of ASCollectionElements, for each item.
* @param supplementaryElements A dictionary of gathered supplementary elements.
*/
- (instancetype)initWithSections:(NSArray<ASSection *> *)sections
items:(ASCollectionElementTwoDimensionalArray *)items
supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements;
@end
NS_ASSUME_NONNULL_END
#endif

View File

@ -1,280 +0,0 @@
//
// ASElementMap.mm
// 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
#import <AsyncDisplayKit/ASElementMap.h>
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASCollectionElement.h>
#import <AsyncDisplayKit/ASTwoDimensionalArrayUtils.h>
#import <AsyncDisplayKit/ASMutableElementMap.h>
#import <AsyncDisplayKit/ASSection.h>
#import <AsyncDisplayKit/NSIndexSet+ASHelpers.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
@interface ASElementMap () <ASDescriptionProvider>
@property (nonatomic, readonly) NSArray<ASSection *> *sections;
// Element -> IndexPath
@property (nonatomic, readonly) NSMapTable<ASCollectionElement *, NSIndexPath *> *elementToIndexPathMap;
// The items, in a 2D array
@property (nonatomic, readonly) ASCollectionElementTwoDimensionalArray *sectionsOfItems;
@property (nonatomic, readonly) ASSupplementaryElementDictionary *supplementaryElements;
@end
@implementation ASElementMap
- (instancetype)init
{
return [self initWithSections:@[] items:@[] supplementaryElements:@{}];
}
- (instancetype)initWithSections:(NSArray<ASSection *> *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements
{
NSCParameterAssert(items.count == sections.count);
if (self = [super init]) {
_sections = [sections copy];
_sectionsOfItems = [[NSArray alloc] initWithArray:items copyItems:YES];
_supplementaryElements = [[NSDictionary alloc] initWithDictionary:supplementaryElements copyItems:YES];
// Setup our index path map
_elementToIndexPathMap = [NSMapTable mapTableWithKeyOptions:(NSMapTableStrongMemory | NSMapTableObjectPointerPersonality) valueOptions:NSMapTableCopyIn];
NSInteger s = 0;
for (NSArray *section in _sectionsOfItems) {
NSInteger i = 0;
for (ASCollectionElement *element in section) {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:s];
[_elementToIndexPathMap setObject:indexPath forKey:element];
i++;
}
s++;
}
for (NSDictionary *supplementariesForKind in [_supplementaryElements objectEnumerator]) {
[supplementariesForKind enumerateKeysAndObjectsUsingBlock:^(NSIndexPath *_Nonnull indexPath, ASCollectionElement * _Nonnull element, BOOL * _Nonnull stop) {
[_elementToIndexPathMap setObject:indexPath forKey:element];
}];
}
}
return self;
}
- (NSUInteger)count
{
return _elementToIndexPathMap.count;
}
- (NSArray<NSIndexPath *> *)itemIndexPaths
{
return ASIndexPathsForTwoDimensionalArray(_sectionsOfItems);
}
- (NSArray<ASCollectionElement *> *)itemElements
{
return ASElementsInTwoDimensionalArray(_sectionsOfItems);
}
- (NSInteger)numberOfSections
{
return _sectionsOfItems.count;
}
- (NSArray<NSString *> *)supplementaryElementKinds
{
return _supplementaryElements.allKeys;
}
- (NSInteger)numberOfItemsInSection:(NSInteger)section
{
if (![self sectionIndexIsValid:section assert:YES]) {
return 0;
}
return _sectionsOfItems[section].count;
}
- (id<ASSectionContext>)contextForSection:(NSInteger)section
{
if (![self sectionIndexIsValid:section assert:NO]) {
return nil;
}
return _sections[section].context;
}
- (nullable NSIndexPath *)indexPathForElement:(ASCollectionElement *)element
{
return element ? [_elementToIndexPathMap objectForKey:element] : nil;
}
- (nullable NSIndexPath *)indexPathForElementIfCell:(ASCollectionElement *)element
{
if (element.supplementaryElementKind == nil) {
return [self indexPathForElement:element];
} else {
return nil;
}
}
- (nullable ASCollectionElement *)elementForItemAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger section, item;
if (![self itemIndexPathIsValid:indexPath assert:NO item:&item section:&section]) {
return nil;
}
return _sectionsOfItems[section][item];
}
- (nullable ASCollectionElement *)supplementaryElementOfKind:(NSString *)supplementaryElementKind atIndexPath:(NSIndexPath *)indexPath
{
return _supplementaryElements[supplementaryElementKind][indexPath];
}
- (ASCollectionElement *)elementForLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
switch (layoutAttributes.representedElementCategory) {
case UICollectionElementCategoryCell:
// Cell
return [self elementForItemAtIndexPath:layoutAttributes.indexPath];
case UICollectionElementCategorySupplementaryView:
// Supplementary element.
return [self supplementaryElementOfKind:layoutAttributes.representedElementKind atIndexPath:layoutAttributes.indexPath];
case UICollectionElementCategoryDecorationView:
// No support for decoration views.
return nil;
}
}
- (NSIndexPath *)convertIndexPath:(NSIndexPath *)indexPath fromMap:(ASElementMap *)map
{
if (indexPath.item == NSNotFound) {
// Section index path
NSInteger result = [self convertSection:indexPath.section fromMap:map];
return (result != NSNotFound ? [NSIndexPath indexPathWithIndex:result] : nil);
} else {
// Item index path
ASCollectionElement *element = [map elementForItemAtIndexPath:indexPath];
return [self indexPathForElement:element];
}
}
- (NSInteger)convertSection:(NSInteger)sectionIndex fromMap:(ASElementMap *)map
{
if (![map sectionIndexIsValid:sectionIndex assert:YES]) {
return NSNotFound;
}
ASSection *section = map.sections[sectionIndex];
return [_sections indexOfObjectIdenticalTo:section];
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
// NSMutableCopying conformance is declared in ASMutableElementMap.h, so that most consumers of ASElementMap don't bother with it.
#pragma mark - NSMutableCopying
- (id)mutableCopyWithZone:(NSZone *)zone
{
return [[ASMutableElementMap alloc] initWithSections:_sections items:_sectionsOfItems supplementaryElements:_supplementaryElements];
}
#pragma mark - NSFastEnumeration
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id _Nullable __unsafe_unretained [])buffer count:(NSUInteger)len
{
return [_elementToIndexPathMap countByEnumeratingWithState:state objects:buffer count:len];
}
- (NSString *)smallDescription
{
NSMutableArray *sectionDescriptions = [NSMutableArray array];
NSUInteger i = 0;
for (NSArray *section in _sectionsOfItems) {
[sectionDescriptions addObject:[NSString stringWithFormat:@"<S%tu: %tu>", i, section.count]];
i++;
}
return ASObjectDescriptionMakeWithoutObject(@[ @{ @"itemCounts": sectionDescriptions }]);
}
#pragma mark - ASDescriptionProvider
- (NSString *)description
{
return ASObjectDescriptionMake(self, [self propertiesForDescription]);
}
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
NSMutableArray *result = [NSMutableArray array];
[result addObject:@{ @"items" : _sectionsOfItems }];
[result addObject:@{ @"supplementaryElements" : _supplementaryElements }];
return result;
}
#pragma mark - Internal
/**
* Fails assert + return NO if section is out of bounds.
*/
- (BOOL)sectionIndexIsValid:(NSInteger)section assert:(BOOL)assert
{
NSInteger sectionCount = _sectionsOfItems.count;
if (section >= sectionCount || section < 0) {
if (assert) {
ASDisplayNodeFailAssert(@"Invalid section index %ld when there are only %ld sections!", (long)section, (long)sectionCount);
}
return NO;
} else {
return YES;
}
}
/**
* If indexPath is nil, just returns NO.
* If indexPath is invalid, fails assertion and returns NO.
* Otherwise returns YES and sets the item & section.
*/
- (BOOL)itemIndexPathIsValid:(NSIndexPath *)indexPath assert:(BOOL)assert item:(out NSInteger *)outItem section:(out NSInteger *)outSection
{
if (indexPath == nil) {
return NO;
}
NSInteger section = indexPath.section;
if (![self sectionIndexIsValid:section assert:assert]) {
return NO;
}
NSInteger itemCount = _sectionsOfItems[section].count;
NSInteger item = indexPath.item;
if (item >= itemCount || item < 0) {
if (assert) {
ASDisplayNodeFailAssert(@"Invalid item index %ld in section %ld which only has %ld items!", (long)item, (long)section, (long)itemCount);
}
return NO;
}
*outItem = item;
*outSection = section;
return YES;
}
@end
#endif

View File

@ -1,121 +0,0 @@
//
// ASEventLog.mm
// 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/ASEventLog.h>
#import <AsyncDisplayKit/ASThread.h>
#import <AsyncDisplayKit/ASTraceEvent.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
@implementation ASEventLog {
AS::RecursiveMutex __instanceLock__;
// The index of the most recent log entry. -1 until first entry.
NSInteger _eventLogHead;
// A description of the object we're logging for. This is immutable.
NSString *_objectDescription;
}
/**
* Even just when debugging, all these events can take up considerable memory.
* Store them in a shared NSCache to limit the total consumption.
*/
+ (NSCache<ASEventLog *, NSMutableArray<ASTraceEvent *> *> *)contentsCache
{
static NSCache *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [[NSCache alloc] init];
});
return cache;
}
- (instancetype)initWithObject:(id)anObject
{
if ((self = [super init])) {
_objectDescription = ASObjectDescriptionMakeTiny(anObject);
_eventLogHead = -1;
}
return self;
}
- (instancetype)init
{
// This method is marked unavailable so the compiler won't let them call it.
ASDisplayNodeFailAssert(@"Failed to call initWithObject:");
return nil;
}
- (void)logEventWithBacktrace:(NSArray<NSString *> *)backtrace format:(NSString *)format, ...
{
va_list args;
va_start(args, format);
ASTraceEvent *event = [[ASTraceEvent alloc] initWithBacktrace:backtrace
format:format
arguments:args];
va_end(args);
AS::MutexLocker l(__instanceLock__);
NSCache *cache = [ASEventLog contentsCache];
NSMutableArray<ASTraceEvent *> *events = [cache objectForKey:self];
if (events == nil) {
events = [NSMutableArray arrayWithObject:event];
[cache setObject:events forKey:self];
_eventLogHead = 0;
return;
}
// Increment the head index.
_eventLogHead = (_eventLogHead + 1) % ASEVENTLOG_CAPACITY;
if (_eventLogHead < events.count) {
[events replaceObjectAtIndex:_eventLogHead withObject:event];
} else {
[events insertObject:event atIndex:_eventLogHead];
}
}
- (NSArray<ASTraceEvent *> *)events
{
NSMutableArray<ASTraceEvent *> *events = [[ASEventLog contentsCache] objectForKey:self];
if (events == nil) {
return nil;
}
AS::MutexLocker l(__instanceLock__);
NSUInteger tail = (_eventLogHead + 1);
NSUInteger count = events.count;
NSMutableArray<ASTraceEvent *> *result = [NSMutableArray array];
// Start from `tail` and go through array, wrapping around when we exceed end index.
for (NSUInteger actualIndex = 0; actualIndex < ASEVENTLOG_CAPACITY; actualIndex++) {
NSInteger ringIndex = (tail + actualIndex) % ASEVENTLOG_CAPACITY;
if (ringIndex < count) {
[result addObject:events[ringIndex]];
}
}
return result;
}
- (NSString *)description
{
/**
* This description intentionally doesn't follow the standard description format.
* Since this is a log, it's important for the description to look a certain way, and
* the formal description style doesn't allow for newlines and has a ton of punctuation.
*/
NSArray *events = [self events];
if (events == nil) {
return [NSString stringWithFormat:@"Event log for %@ was purged to conserve memory.", _objectDescription];
} else {
return [NSString stringWithFormat:@"Event log for %@. Events: %@", _objectDescription, events];
}
}
@end

View File

@ -1,51 +0,0 @@
//
// ASHighlightOverlayLayer.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 <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
NS_ASSUME_NONNULL_BEGIN
AS_SUBCLASSING_RESTRICTED
@interface ASHighlightOverlayLayer : CALayer
/**
@summary Initializes with CGRects for the highlighting, in the targetLayer's coordinate space.
@desc This is the designated initializer.
@param rects Array containing CGRects wrapped in NSValue.
@param targetLayer The layer that the rects are relative to. The rects will be translated to the receiver's coordinate space when rendering.
*/
- (instancetype)initWithRects:(NSArray<NSValue *> *)rects targetLayer:(nullable CALayer *)targetLayer;
/**
@summary Initializes with CGRects for the highlighting, in the receiver's coordinate space.
@param rects Array containing CGRects wrapped in NSValue.
*/
- (instancetype)initWithRects:(NSArray<NSValue *> *)rects;
@property (nullable, nonatomic) __attribute__((NSObject)) CGColorRef highlightColor;
@property (nonatomic, weak) CALayer *targetLayer;
@end
@interface CALayer (ASHighlightOverlayLayerSupport)
/**
@summary Set to YES to indicate to a sublayer that this is where highlight overlay layers (for pressed states) should
be added so that the highlight won't be clipped by a neighboring layer.
*/
@property (nonatomic, setter=as_setAllowsHighlightDrawing:) BOOL as_allowsHighlightDrawing;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,134 +0,0 @@
//
// ASHighlightOverlayLayer.mm
// 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/ASHighlightOverlayLayer.h>
#import <UIKit/UIKit.h>
#import <tgmath.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
static const CGFloat kCornerRadius = 2.5;
static const UIEdgeInsets padding = {2, 4, 1.5, 4};
@implementation ASHighlightOverlayLayer
{
NSArray *_rects;
}
+ (id)defaultValueForKey:(NSString *)key
{
if ([key isEqualToString:@"contentsScale"]) {
return @(ASScreenScale());
} else if ([key isEqualToString:@"highlightColor"]) {
CGFloat components[] = {0, 0, 0, 0.25};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef color = CGColorCreate(colorSpace, components);
CGColorSpaceRelease(colorSpace);
return CFBridgingRelease(color);
} else {
return [super defaultValueForKey:key];
}
}
+ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([key isEqualToString:@"bounds"]) {
return YES;
} else {
return [super needsDisplayForKey:key];
}
}
+ (id<CAAction>)defaultActionForKey:(NSString *)event
{
return (id<CAAction>)[NSNull null];
}
- (instancetype)initWithRects:(NSArray *)rects
{
return [self initWithRects:rects targetLayer:nil];
}
- (instancetype)initWithRects:(NSArray *)rects targetLayer:(id)targetLayer
{
if (self = [super init]) {
_rects = [rects copy];
_targetLayer = targetLayer;
}
return self;
}
@dynamic highlightColor;
- (void)drawInContext:(CGContextRef)ctx
{
[super drawInContext:ctx];
CGAffineTransform affine = CGAffineTransformIdentity;
CGMutablePathRef highlightPath = CGPathCreateMutable();
CALayer *targetLayer = self.targetLayer;
for (NSValue *value in _rects) {
CGRect rect = [value CGRectValue];
// Don't highlight empty rects.
if (CGRectIsEmpty(rect)) {
continue;
}
if (targetLayer != nil) {
rect = [self convertRect:rect fromLayer:targetLayer];
}
rect = CGRectMake(std::round(rect.origin.x), std::round(rect.origin.y), std::round(rect.size.width), std::round(rect.size.height));
CGFloat minX = rect.origin.x - padding.left;
CGFloat maxX = CGRectGetMaxX(rect) + padding.right;
CGFloat midX = (maxX - minX) / 2 + minX;
CGFloat minY = rect.origin.y - padding.top;
CGFloat maxY = CGRectGetMaxY(rect) + padding.bottom;
CGFloat midY = (maxY - minY) / 2 + minY;
CGPathMoveToPoint(highlightPath, &affine, minX, midY);
CGPathAddArcToPoint(highlightPath, &affine, minX, maxY, midX, maxY, kCornerRadius);
CGPathAddArcToPoint(highlightPath, &affine, maxX, maxY, maxX, midY, kCornerRadius);
CGPathAddArcToPoint(highlightPath, &affine, maxX, minY, midX, minY, kCornerRadius);
CGPathAddArcToPoint(highlightPath, &affine, minX, minY, minX, midY, kCornerRadius);
CGPathCloseSubpath(highlightPath);
}
CGContextAddPath(ctx, highlightPath);
CGContextSetFillColorWithColor(ctx, self.highlightColor);
CGContextDrawPath(ctx, kCGPathFill);
CGPathRelease(highlightPath);
}
- (CALayer *)hitTest:(CGPoint)p
{
// Don't handle taps
return nil;
}
@end
@implementation CALayer (ASHighlightOverlayLayerSupport)
static NSString *kAllowsHighlightDrawingKey = @"allows_highlight_drawing";
- (BOOL)as_allowsHighlightDrawing
{
return [[self valueForKey:kAllowsHighlightDrawingKey] boolValue];
}
- (void)as_setAllowsHighlightDrawing:(BOOL)allowsHighlightDrawing
{
[self setValue:@(allowsHighlightDrawing) forKey:kAllowsHighlightDrawingKey];
}
@end

View File

@ -1,38 +0,0 @@
//
// ASImageContainerProtocolCategories.mm
// 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/ASImageContainerProtocolCategories.h>
@implementation UIImage (ASImageContainerProtocol)
- (UIImage *)asdk_image
{
return self;
}
- (NSData *)asdk_animatedImageData
{
return nil;
}
@end
@implementation NSData (ASImageContainerProtocol)
- (UIImage *)asdk_image
{
return nil;
}
- (NSData *)asdk_animatedImageData
{
return self;
}
@end

View File

@ -1,240 +0,0 @@
//
// ASImageProtocols.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
@protocol ASAnimatedImageProtocol;
@protocol ASImageContainerProtocol <NSObject>
- (nullable UIImage *)asdk_image;
- (nullable NSData *)asdk_animatedImageData;
@end
typedef void(^ASImageCacherCompletion)(id <ASImageContainerProtocol> _Nullable imageFromCache);
@protocol ASImageCacheProtocol <NSObject>
/**
@abstract Attempts to fetch an image with the given URL from the cache.
@param URL The URL of the image to retrieve from the cache.
@param callbackQueue The queue to call `completion` on.
@param completion The block to be called when the cache has either hit or missed.
@discussion If `URL` is nil, `completion` will be invoked immediately with a nil image. This method should not block
the calling thread as it is likely to be called from the main thread.
*/
- (void)cachedImageWithURL:(NSURL *)URL
callbackQueue:(dispatch_queue_t)callbackQueue
completion:(ASImageCacherCompletion)completion;
@optional
/**
@abstract Attempts to fetch an image with the given URL from a memory cache.
@param URL The URL of the image to retrieve from the cache.
@discussion This method exists to support synchronous rendering of nodes. Before the layer is drawn, this method
is called to attempt to get the image out of the cache synchronously. This allows drawing to occur on the main thread
if displaysAsynchronously is set to NO or recursivelyEnsureDisplaySynchronously: has been called.
This method *should* block the calling thread to fetch the image from a fast memory cache. It is OK to return nil from
this method and instead support only cachedImageWithURL:callbackQueue:completion: however, synchronous rendering will
not be possible.
*/
- (nullable id <ASImageContainerProtocol>)synchronouslyFetchedCachedImageWithURL:(NSURL *)URL;
/**
@abstract Called during clearPreloadedData. Allows the cache to optionally trim items.
@note Depending on your caches implementation you may *not* wish to respond to this method. It is however useful
if you have a memory and disk cache in which case you'll likely want to clear out the memory cache.
*/
- (void)clearFetchedImageFromCacheWithURL:(NSURL *)URL;
@end
/**
@param image The image that was downloaded, if the image could be successfully downloaded; nil otherwise.
@param error An error describing why the download of `URL` failed, if the download failed; nil otherwise.
@param downloadIdentifier The identifier for the download task that completed.
@param userInfo Any additional info that your downloader would like to communicate through Texture.
*/
typedef void(^ASImageDownloaderCompletion)(id <ASImageContainerProtocol> _Nullable image, NSError * _Nullable error, id _Nullable downloadIdentifier, id _Nullable userInfo);
/**
@param progress The progress of the download, in the range of (0.0, 1.0), inclusive.
*/
typedef void(^ASImageDownloaderProgress)(CGFloat progress);
typedef void(^ASImageDownloaderProgressImage)(UIImage *progressImage, CGFloat progress, id _Nullable downloadIdentifier);
typedef NS_ENUM(NSUInteger, ASImageDownloaderPriority) {
ASImageDownloaderPriorityPreload = 0,
ASImageDownloaderPriorityImminent,
ASImageDownloaderPriorityVisible
};
@protocol ASImageDownloaderProtocol <NSObject>
@required
/**
@abstract Downloads an image with the given URL.
@param URL The URL of the image to download.
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
@param downloadProgress The block to be invoked when the download of `URL` progresses.
@param completion The block to be invoked when the download has completed, or has failed.
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
retain the identifier if you wish to use it later.
*/
- (nullable id)downloadImageWithURL:(NSURL *)URL
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
completion:(ASImageDownloaderCompletion)completion;
/**
@abstract Cancels an image download.
@param downloadIdentifier The opaque download identifier object returned from
`downloadImageWithURL:callbackQueue:downloadProgress:completion:`.
@discussion This method has no effect if `downloadIdentifier` is nil.
*/
- (void)cancelImageDownloadForIdentifier:(id)downloadIdentifier;
@optional
/**
@abstract Downloads an image with the given URL.
@param URL The URL of the image to download.
@param priority The priority at which the image should be downloaded.
@param callbackQueue The queue to call `downloadProgressBlock` and `completion` on.
@param downloadProgress The block to be invoked when the download of `URL` progresses.
@param completion The block to be invoked when the download has completed, or has failed.
@discussion This method is likely to be called on the main thread, so any custom implementations should make sure to background any expensive download operations.
@note If this method is implemented, it will be called instead of the required method (`downloadImageWithURL:callbackQueue:downloadProgress:completion:`).
@result An opaque identifier to be used in canceling the download, via `cancelImageDownloadForIdentifier:`. You must
retain the identifier if you wish to use it later.
*/
- (nullable id)downloadImageWithURL:(NSURL *)URL
priority:(ASImageDownloaderPriority)priority
callbackQueue:(dispatch_queue_t)callbackQueue
downloadProgress:(nullable ASImageDownloaderProgress)downloadProgress
completion:(ASImageDownloaderCompletion)completion;
/**
@abstract Cancels an image download, however indicating resume data should be stored in case of redownload.
@param downloadIdentifier The opaque download identifier object returned from
`downloadImageWithURL:callbackQueue:downloadProgress:completion:`.
@discussion This method has no effect if `downloadIdentifier` is nil. If implemented, this method
may be called instead of `cancelImageDownloadForIdentifier:` in cases where ASDK believes there's a chance
the image download will be resumed (currently when an image exits preload range). You can use this to store
any data that has already been downloaded for use in resuming the download later.
*/
- (void)cancelImageDownloadWithResumePossibilityForIdentifier:(id)downloadIdentifier;
/**
@abstract Return an object that conforms to ASAnimatedImageProtocol
@param animatedImageData Data that represents an animated image.
*/
- (nullable id <ASAnimatedImageProtocol>)animatedImageWithData:(NSData *)animatedImageData;
/**
@abstract Sets block to be called when a progress image is available.
@param progressBlock The block to be invoked when the download has a progressive render of an image available.
@param callbackQueue The queue to call `progressImageBlock` on.
@param downloadIdentifier The opaque download identifier object returned from
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
*/
- (void)setProgressImageBlock:(nullable ASImageDownloaderProgressImage)progressBlock
callbackQueue:(dispatch_queue_t)callbackQueue
withDownloadIdentifier:(id)downloadIdentifier;
/**
@abstract Called to indicate what priority an image should be downloaded at.
@param priority The priority at which the image should be downloaded.
@param downloadIdentifier The opaque download identifier object returned from
`downloadImageWithURL:callbackQueue:downloadProgressBlock:completion:`.
*/
- (void)setPriority:(ASImageDownloaderPriority)priority
withDownloadIdentifier:(id)downloadIdentifier;
@end
@protocol ASAnimatedImageProtocol <NSObject>
@optional
/**
@abstract A block which receives the cover image. Should be called when the objects cover image is ready.
*/
@property (nonatomic) void (^coverImageReadyCallback)(UIImage *coverImage);
/**
@abstract Returns whether the supplied data contains a supported animated image format.
@param data the data to check if contains a supported animated image.
*/
- (BOOL)isDataSupported:(NSData *)data;
@required
/**
@abstract Return the objects's cover image.
*/
@property (nonatomic, readonly) UIImage *coverImage;
/**
@abstract Return a boolean to indicate that the cover image is ready.
*/
@property (nonatomic, readonly) BOOL coverImageReady;
/**
@abstract Return the total duration of the animated image's playback.
*/
@property (nonatomic, readonly) CFTimeInterval totalDuration;
/**
@abstract Return the interval at which playback should occur. Will be set to a CADisplayLink's frame interval.
*/
@property (nonatomic, readonly) NSUInteger frameInterval;
/**
@abstract Return the total number of loops the animated image should play or 0 to loop infinitely.
*/
@property (nonatomic, readonly) size_t loopCount;
/**
@abstract Return the total number of frames in the animated image.
*/
@property (nonatomic, readonly) size_t frameCount;
/**
@abstract Return YES when playback is ready to occur.
*/
@property (nonatomic, readonly) BOOL playbackReady;
/**
@abstract Return any error that has occured. Playback will be paused if this returns non-nil.
*/
@property (nonatomic, readonly) NSError *error;
/**
@abstract Should be called when playback is ready.
*/
@property (nonatomic) dispatch_block_t playbackReadyCallback;
/**
@abstract Return the image at a given index.
*/
- (CGImageRef)imageAtIndex:(NSUInteger)index;
/**
@abstract Return the duration at a given index.
*/
- (CFTimeInterval)durationAtIndex:(NSUInteger)index;
/**
@abstract Clear any cached data. Called when playback is paused.
*/
- (void)clearAnimatedImageCache;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,43 +0,0 @@
//
// ASInsetLayoutSpec.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/ASLayoutSpec.h>
NS_ASSUME_NONNULL_BEGIN
/**
A layout spec that wraps another layoutElement child, applying insets around it.
If the child has a size specified as a fraction, the fraction is resolved against this spec's parent
size **after** applying insets.
@example ASOuterLayoutSpec contains an ASInsetLayoutSpec with an ASInnerLayoutSpec. Suppose that:
- ASOuterLayoutSpec is 200pt wide.
- ASInnerLayoutSpec specifies its width as 100%.
- The ASInsetLayoutSpec has insets of 10pt on every side.
ASInnerLayoutSpec will have size 180pt, not 200pt, because it receives a parent size that has been adjusted for insets.
If you're familiar with CSS: ASInsetLayoutSpec's child behaves similarly to "box-sizing: border-box".
An infinite inset is resolved as an inset equal to all remaining space after applying the other insets and child size.
@example An ASInsetLayoutSpec with an infinite left inset and 10px for all other edges will position it's child 10px from the right edge.
*/
@interface ASInsetLayoutSpec : ASLayoutSpec
@property (nonatomic) UIEdgeInsets insets;
/**
@param insets The amount of space to inset on each side.
@param child The wrapped child to inset.
*/
+ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutElement>)child NS_RETURNS_RETAINED AS_WARN_UNUSED_RESULT;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,123 +0,0 @@
//
// ASInsetLayoutSpec.mm
// 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/ASInsetLayoutSpec.h>
#import <AsyncDisplayKit/ASLayoutSpec+Subclasses.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASInternalHelpers.h>
@interface ASInsetLayoutSpec ()
{
UIEdgeInsets _insets;
}
@end
/* Returns f if f is finite, substitute otherwise */
static CGFloat finite(CGFloat f, CGFloat substitute)
{
return isinf(f) ? substitute : f;
}
/* Returns f if f is finite, 0 otherwise */
static CGFloat finiteOrZero(CGFloat f)
{
return finite(f, 0);
}
/* Returns the inset required to center 'inner' in 'outer' */
static CGFloat centerInset(CGFloat outer, CGFloat inner)
{
return ASRoundPixelValue((outer - inner) / 2);
}
@implementation ASInsetLayoutSpec
- (instancetype)initWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutElement>)child;
{
if (!(self = [super init])) {
return nil;
}
ASDisplayNodeAssertNotNil(child, @"Child cannot be nil");
_insets = insets;
[self setChild:child];
return self;
}
+ (instancetype)insetLayoutSpecWithInsets:(UIEdgeInsets)insets child:(id<ASLayoutElement>)child NS_RETURNS_RETAINED
{
return [[self alloc] initWithInsets:insets child:child];
}
- (void)setInsets:(UIEdgeInsets)insets
{
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
_insets = insets;
}
/**
Inset will compute a new constrained size for it's child after applying insets and re-positioning
the child to respect the inset.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutElementSize)size
relativeToParentSize:(CGSize)parentSize
{
if (self.child == nil) {
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing.");
return [ASLayout layoutWithLayoutElement:self size:CGSizeZero];
}
const CGFloat insetsX = (finiteOrZero(_insets.left) + finiteOrZero(_insets.right));
const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom));
// if either x-axis inset is infinite, let child be intrinsic width
const CGFloat minWidth = (isinf(_insets.left) || isinf(_insets.right)) ? 0 : constrainedSize.min.width;
// if either y-axis inset is infinite, let child be intrinsic height
const CGFloat minHeight = (isinf(_insets.top) || isinf(_insets.bottom)) ? 0 : constrainedSize.min.height;
const ASSizeRange insetConstrainedSize = {
{
MAX(0, minWidth - insetsX),
MAX(0, minHeight - insetsY),
},
{
MAX(0, constrainedSize.max.width - insetsX),
MAX(0, constrainedSize.max.height - insetsY),
}
};
const CGSize insetParentSize = {
MAX(0, parentSize.width - insetsX),
MAX(0, parentSize.height - insetsY)
};
ASLayout *sublayout = [self.child layoutThatFits:insetConstrainedSize parentSize:insetParentSize];
const CGSize computedSize = ASSizeRangeClamp(constrainedSize, {
finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width),
finite(sublayout.size.height + _insets.top + _insets.bottom, constrainedSize.max.height),
});
const CGFloat x = finite(_insets.left, constrainedSize.max.width -
(finite(_insets.right,
centerInset(constrainedSize.max.width, sublayout.size.width)) + sublayout.size.width));
const CGFloat y = finite(_insets.top,
constrainedSize.max.height -
(finite(_insets.bottom,
centerInset(constrainedSize.max.height, sublayout.size.height)) + sublayout.size.height));
sublayout.position = CGPointMake(x, y);
return [ASLayout layoutWithLayoutElement:self size:computedSize sublayouts:@[sublayout]];
}
@end

View File

@ -1,68 +0,0 @@
//
// ASIntegerMap.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>
NS_ASSUME_NONNULL_BEGIN
/**
* An objective-C wrapper for unordered_map.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASIntegerMap : NSObject <NSCopying>
/**
* Creates a map based on the specified update to an array.
*
* If oldCount is 0, returns the empty map.
* If deleted and inserted are empty, returns the identity map.
*/
+ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount
deleted:(nullable NSIndexSet *)deleted
inserted:(nullable NSIndexSet *)inserted NS_RETURNS_RETAINED;
/**
* A singleton that maps each integer to itself. Its inverse is itself.
*
* Note: You cannot mutate this.
*/
@property (class, readonly) ASIntegerMap *identityMap;
+ (ASIntegerMap *)identityMap NS_RETURNS_RETAINED;
/**
* A singleton that returns NSNotFound for all keys. Its inverse is itself.
*
* Note: You cannot mutate this.
*/
@property (class, readonly) ASIntegerMap *emptyMap;
+ (ASIntegerMap *)emptyMap NS_RETURNS_RETAINED;
/**
* Retrieves the integer for a given key, or NSNotFound if the key is not found.
*
* @param key A key to lookup the value for.
*/
- (NSInteger)integerForKey:(NSInteger)key;
/**
* Sets the value for a given key.
*
* @param value The new value.
* @param key The key to store the value for.
*/
- (void)setInteger:(NSInteger)value forKey:(NSInteger)key;
/**
* Create and return a map with the inverse mapping.
*/
- (ASIntegerMap *)inverseMap;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,185 +0,0 @@
//
// ASIntegerMap.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASIntegerMap.h>
#import <AsyncDisplayKit/ASAssert.h>
#import <unordered_map>
#import <AsyncDisplayKit/NSIndexSet+ASHelpers.h>
#import <AsyncDisplayKit/ASObjectDescriptionHelpers.h>
/**
* This is just a friendly Objective-C interface to unordered_map<NSInteger, NSInteger>
*/
@interface ASIntegerMap () <ASDescriptionProvider>
@end
@implementation ASIntegerMap {
std::unordered_map<NSInteger, NSInteger> _map;
BOOL _isIdentity;
BOOL _isEmpty;
BOOL _immutable; // identity map and empty mape are immutable.
}
#pragma mark - Singleton
+ (ASIntegerMap *)identityMap NS_RETURNS_RETAINED
{
static ASIntegerMap *identityMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
identityMap = [[ASIntegerMap alloc] init];
identityMap->_isIdentity = YES;
identityMap->_immutable = YES;
});
return identityMap;
}
+ (ASIntegerMap *)emptyMap NS_RETURNS_RETAINED
{
static ASIntegerMap *emptyMap;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
emptyMap = [[ASIntegerMap alloc] init];
emptyMap->_isEmpty = YES;
emptyMap->_immutable = YES;
});
return emptyMap;
}
+ (ASIntegerMap *)mapForUpdateWithOldCount:(NSInteger)oldCount deleted:(NSIndexSet *)deletions inserted:(NSIndexSet *)insertions NS_RETURNS_RETAINED
{
if (oldCount == 0) {
return ASIntegerMap.emptyMap;
}
if (deletions.count == 0 && insertions.count == 0) {
return ASIntegerMap.identityMap;
}
ASIntegerMap *result = [[ASIntegerMap alloc] init];
// Start with the old indexes
NSMutableIndexSet *indexes = [NSMutableIndexSet indexSetWithIndexesInRange:NSMakeRange(0, oldCount)];
// Descending order, shift deleted ranges left
[deletions enumerateRangesWithOptions:NSEnumerationReverse usingBlock:^(NSRange range, BOOL * _Nonnull stop) {
[indexes shiftIndexesStartingAtIndex:NSMaxRange(range) by:-range.length];
}];
// Ascending order, shift inserted ranges right
[insertions enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
[indexes shiftIndexesStartingAtIndex:range.location by:range.length];
}];
__block NSInteger oldIndex = 0;
[indexes enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
// Note we advance oldIndex unconditionally, not newIndex
for (NSInteger newIndex = range.location; newIndex < NSMaxRange(range); oldIndex++) {
if ([deletions containsIndex:oldIndex]) {
// index was deleted, do nothing, just let oldIndex advance.
} else {
// assign the next index for this item.
result->_map[oldIndex] = newIndex++;
}
}
}];
return result;
}
- (NSInteger)integerForKey:(NSInteger)key
{
if (_isIdentity) {
return key;
} else if (_isEmpty) {
return NSNotFound;
}
const auto result = _map.find(key);
return result != _map.end() ? result->second : NSNotFound;
}
- (void)setInteger:(NSInteger)value forKey:(NSInteger)key
{
if (_immutable) {
ASDisplayNodeFailAssert(@"Cannot mutate special integer map: %@", self);
return;
}
_map[key] = value;
}
- (ASIntegerMap *)inverseMap
{
if (_isIdentity || _isEmpty) {
return self;
}
const auto result = [[ASIntegerMap alloc] init];
for (const auto &e : _map) {
result->_map[e.second] = e.first;
}
return result;
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
if (_immutable) {
return self;
}
const auto newMap = [[ASIntegerMap allocWithZone:zone] init];
newMap->_map = _map;
return newMap;
}
#pragma mark - Description
- (NSMutableArray<NSDictionary *> *)propertiesForDescription
{
NSMutableArray *result = [NSMutableArray array];
if (_isIdentity) {
[result addObject:@{ @"map": @"<identity>" }];
} else if (_isEmpty) {
[result addObject:@{ @"map": @"<empty>" }];
} else {
// { 1->2 3->4 5->6 }
NSMutableString *str = [NSMutableString string];
for (const auto &e : _map) {
[str appendFormat:@" %ld->%ld", (long)e.first, (long)e.second];
}
// Remove leading space
if (str.length > 0) {
[str deleteCharactersInRange:NSMakeRange(0, 1)];
}
[result addObject:@{ @"map": str }];
}
return result;
}
- (NSString *)description
{
return ASObjectDescriptionMakeWithoutObject([self propertiesForDescription]);
}
- (BOOL)isEqual:(id)object
{
if ([super isEqual:object]) {
return YES;
}
if (ASIntegerMap *otherMap = ASDynamicCast(object, ASIntegerMap)) {
return otherMap->_map == _map;
}
return NO;
}
@end

View File

@ -1,40 +0,0 @@
//
// ASLayoutController.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/ASLayoutRangeType.h>
#import <AsyncDisplayKit/ASScrollDirection.h>
NS_ASSUME_NONNULL_BEGIN
@class ASCollectionElement, ASElementMap;
struct ASDirectionalScreenfulBuffer {
CGFloat positiveDirection; // Positive relative to iOS Core Animation layer coordinate space.
CGFloat negativeDirection;
};
typedef struct ASDirectionalScreenfulBuffer ASDirectionalScreenfulBuffer;
@protocol ASLayoutController <NSObject>
- (void)setTuningParameters:(ASRangeTuningParameters)tuningParameters forRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
- (ASRangeTuningParameters)tuningParametersForRangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType;
- (NSHashTable<ASCollectionElement *> *)elementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode rangeType:(ASLayoutRangeType)rangeType map:(ASElementMap *)map;
- (void)allElementsForScrolling:(ASScrollDirection)scrollDirection rangeMode:(ASLayoutRangeMode)rangeMode displaySet:(NSHashTable<ASCollectionElement *> * _Nullable * _Nullable)displaySet preloadSet:(NSHashTable<ASCollectionElement *> * _Nullable * _Nullable)preloadSet map:(ASElementMap *)map;
@optional
@end
NS_ASSUME_NONNULL_END

View File

@ -1,163 +0,0 @@
//
// ASLog.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 <AsyncDisplayKit/ASBaseDefines.h>
#import <Foundation/Foundation.h>
#import <os/log.h>
#import <os/activity.h>
#ifndef ASEnableVerboseLogging
#define ASEnableVerboseLogging 0
#endif
/**
* Disable all logging.
*
* You should only use this function if the default log level is
* annoying during development. By default, logging is run at
* the appropriate system log level (see the os_log_* functions),
* so you do not need to worry generally about the performance
* implications of log messages.
*
* For example, virtually all log messages generated by Texture
* are at the `debug` log level, which the system
* disables in production.
*/
AS_EXTERN void ASDisableLogging(void);
/**
* Restore logging that has been runtime-disabled via ASDisableLogging().
*
* Logging can be disabled at runtime using the ASDisableLogging() function.
* This command restores logging to the level provided in the build
* configuration. This can be used in conjunction with ASDisableLogging()
* to allow logging to be toggled off and back on at runtime.
*/
AS_EXTERN void ASEnableLogging(void);
/// Log for general node events e.g. interfaceState, didLoad.
#define ASNodeLogEnabled 1
AS_EXTERN os_log_t ASNodeLog(void);
/// Log for layout-specific events e.g. calculateLayout.
#define ASLayoutLogEnabled 1
AS_EXTERN os_log_t ASLayoutLog(void);
/// Log for display-specific events e.g. display queue batches.
#define ASDisplayLogEnabled 1
AS_EXTERN os_log_t ASDisplayLog(void);
/// Log for collection events e.g. reloadData, performBatchUpdates.
#define ASCollectionLogEnabled 1
AS_EXTERN os_log_t ASCollectionLog(void);
/// Log for ASNetworkImageNode and ASMultiplexImageNode events.
#define ASImageLoadingLogEnabled 1
AS_EXTERN os_log_t ASImageLoadingLog(void);
/// Specialized log for our main thread deallocation trampoline.
#define ASMainThreadDeallocationLogEnabled 0
AS_EXTERN os_log_t ASMainThreadDeallocationLog(void);
/**
* The activity tracing system changed a lot between iOS 9 and 10.
* In iOS 10, the system was merged with logging and became much more powerful
* and adopted a new API.
*
* The legacy API is visible, but its functionality is extremely limited and the API is so different
* that we don't bother with it. For example, activities described by os_activity_start/end are not
* reflected in the log whereas activities described by the newer
* os_activity_scope are. So unfortunately we must use these iOS 10
* APIs to get meaningful logging data.
*/
#if OS_LOG_TARGET_HAS_10_12_FEATURES
#define OS_ACTIVITY_NULLABLE nullable
#define AS_ACTIVITY_CURRENT OS_ACTIVITY_CURRENT
#define as_activity_scope(activity) os_activity_scope(activity)
#define as_activity_apply(activity, block) os_activity_apply(activity, block)
#define as_activity_create(description, parent_activity, flags) os_activity_create(description, parent_activity, flags)
#define as_activity_scope_enter(activity, statePtr) os_activity_scope_enter(activity, statePtr)
#define as_activity_scope_leave(statePtr) os_activity_scope_leave(statePtr)
#define as_activity_get_identifier(activity, outParentID) os_activity_get_identifier(activity, outParentID)
#else
#define OS_ACTIVITY_NULLABLE
#define AS_ACTIVITY_CURRENT OS_ACTIVITY_NULL
#define as_activity_scope(activity)
#define as_activity_apply(activity, block)
#define as_activity_create(description, parent_activity, flags) OS_ACTIVITY_NULL
#define as_activity_scope_enter(activity, statePtr)
#define as_activity_scope_leave(statePtr)
#define as_activity_get_identifier(activity, outParentID) (os_activity_id_t)0
#endif // OS_LOG_TARGET_HAS_10_12_FEATURES
// Create activities only when verbose enabled. Doesn't materially impact performance, but good if we're cluttering up
// activity scopes and reducing readability.
#if ASEnableVerboseLogging
#define as_activity_scope_verbose(activity) as_activity_scope(activity)
#else
#define as_activity_scope_verbose(activity)
#endif
// Convenience for: as_activity_scope(as_activity_create(description, AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT))
#define as_activity_create_for_scope(description) \
as_activity_scope(as_activity_create(description, AS_ACTIVITY_CURRENT, OS_ACTIVITY_FLAG_DEFAULT))
/**
* The logging macros are not guarded by deployment-target checks like the activity macros are, but they are
* only available on iOS >= 9 at runtime, so just make them conditional.
*/
#define as_log_create(subsystem, category) ({ \
os_log_t __val; \
if (AS_AVAILABLE_IOS_TVOS(9, 9)) { \
__val = os_log_create(subsystem, category); \
} else { \
__val = (os_log_t)0; \
} \
__val; \
})
#define as_log_debug(log, format, ...) \
if (AS_AVAILABLE_IOS_TVOS(9, 9)) { \
os_log_debug(log, format, ##__VA_ARGS__); \
} else { \
(void)0; \
} \
#define as_log_info(log, format, ...) \
if (AS_AVAILABLE_IOS_TVOS(9, 9)) { \
os_log_info(log, format, ##__VA_ARGS__); \
} else { \
(void)0; \
} \
#define as_log_error(log, format, ...) \
if (AS_AVAILABLE_IOS_TVOS(9, 9)) { \
os_log_error(log, format, ##__VA_ARGS__); \
} else { \
(void)0; \
} \
#define as_log_fault(log, format, ...) \
if (AS_AVAILABLE_IOS_TVOS(9, 9)) { \
os_log_fault(log, format, ##__VA_ARGS__); \
} else { \
(void)0; \
} \
#if ASEnableVerboseLogging
#define as_log_verbose(log, format, ...) as_log_debug(log, format, ##__VA_ARGS__)
#else
#define as_log_verbose(log, format, ...)
#endif

View File

@ -1,48 +0,0 @@
//
// ASLog.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/ASLog.h>
#import <stdatomic.h>
static atomic_bool __ASLogEnabled = ATOMIC_VAR_INIT(YES);
void ASDisableLogging() {
atomic_store(&__ASLogEnabled, NO);
}
void ASEnableLogging() {
atomic_store(&__ASLogEnabled, YES);
}
ASDISPLAYNODE_INLINE BOOL ASLoggingIsEnabled() {
return atomic_load(&__ASLogEnabled);
}
os_log_t ASNodeLog() {
return (ASNodeLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Node")) : OS_LOG_DISABLED;
}
os_log_t ASLayoutLog() {
return (ASLayoutLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Layout")) : OS_LOG_DISABLED;
}
os_log_t ASCollectionLog() {
return (ASCollectionLogEnabled && ASLoggingIsEnabled()) ?ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Collection")) : OS_LOG_DISABLED;
}
os_log_t ASDisplayLog() {
return (ASDisplayLogEnabled && ASLoggingIsEnabled()) ?ASCreateOnce(as_log_create("org.TextureGroup.Texture", "Display")) : OS_LOG_DISABLED;
}
os_log_t ASImageLoadingLog() {
return (ASImageLoadingLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "ImageLoading")) : OS_LOG_DISABLED;
}
os_log_t ASMainThreadDeallocationLog() {
return (ASMainThreadDeallocationLogEnabled && ASLoggingIsEnabled()) ? ASCreateOnce(as_log_create("org.TextureGroup.Texture", "MainDealloc")) : OS_LOG_DISABLED;
}

View File

@ -1,65 +0,0 @@
//
// ASMutableAttributedStringBuilder.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
/*
* Use this class to compose new attributed strings. You may use the normal
* attributed string calls on this the same way you would on a normal mutable
* attributed string, but it coalesces your changes into transactions on the
* actual string allowing improvements in performance.
*
* @discussion This is a use-once and throw away class for each string you make.
* Since this class is designed for increasing performance, we actually hand
* back the internally managed mutable attributed string in the
* `composedAttributedString` call. So once you make that call, any more
* changes will actually modify the string that was handed back to you in that
* method.
*
* Combination of multiple calls into single attribution is managed through
* merging of attribute dictionaries over ranges. For best performance, call
* collections of attributions over a single range together. So for instance,
* don't call addAttributes for range1, then range2, then range1 again. Group
* them together so you call addAttributes for both range1 together, and then
* range2.
*
* Also please note that switching between addAttribute and setAttributes in the
* middle of composition is a bad idea for performance because they have
* semantically different meanings, and trigger a commit of the pending
* attributes.
*
* Please note that ALL of the standard NSString methods are left unimplemented.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASMutableAttributedStringBuilder : NSMutableAttributedString
- (instancetype)initWithString:(NSString *)str attributes:(nullable NSDictionary<NSString *, id> *)attrs;
- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr;
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str;
- (void)setAttributes:(nullable NSDictionary<NSString *, id> *)attrs range:(NSRange)range;
- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range;
- (void)addAttributes:(NSDictionary<NSString *, id> *)attrs range:(NSRange)range;
- (void)removeAttribute:(NSString *)name range:(NSRange)range;
- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString;
- (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc;
- (void)appendAttributedString:(NSAttributedString *)attrString;
- (void)deleteCharactersInRange:(NSRange)range;
- (void)setAttributedString:(NSAttributedString *)attrString;
- (NSMutableAttributedString *)composedAttributedString;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,254 +0,0 @@
//
// ASMutableAttributedStringBuilder.mm
// 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/ASMutableAttributedStringBuilder.h>
@implementation ASMutableAttributedStringBuilder {
// Flag for the type of the current transaction (set or add)
BOOL _setRange;
// The range over which the currently pending transaction will occur
NSRange _pendingRange;
// The actual attribute dictionary that is being composed
NSMutableDictionary *_pendingRangeAttributes;
NSMutableAttributedString *_attrStr;
// We delay initialization of the _attrStr until we need to
NSString *_initString;
}
- (instancetype)init
{
if (self = [super init]) {
_attrStr = [[NSMutableAttributedString alloc] init];
_pendingRange.location = NSNotFound;
}
return self;
}
- (instancetype)initWithString:(NSString *)str
{
return [self initWithString:str attributes:@{}];
}
- (instancetype)initWithString:(NSString *)str attributes:(NSDictionary *)attrs
{
if (self = [super init]) {
// We cache this in an ivar that we can lazily construct the attributed
// string with when we get to a forced commit point.
_initString = str;
// Triggers a creation of the _pendingRangeAttributes dictionary which then
// is filled with entries from the given attrs dict.
[[self _pendingRangeAttributes] addEntriesFromDictionary:attrs];
_setRange = NO;
_pendingRange = NSMakeRange(0, _initString.length);
}
return self;
}
- (instancetype)initWithAttributedString:(NSAttributedString *)attrStr
{
if (self = [super init]) {
_attrStr = [[NSMutableAttributedString alloc] initWithAttributedString:attrStr];
_pendingRange.location = NSNotFound;
}
return self;
}
- (NSMutableAttributedString *)_attributedString
{
if (_attrStr == nil && _initString != nil) {
// We can lazily construct the attributed string if it hasn't already been
// created with the existing pending attributes. This is significantly
// faster if more attributes are added after initializing this instance
// and the new attributions are for the entire string anyway.
_attrStr = [[NSMutableAttributedString alloc] initWithString:_initString attributes:_pendingRangeAttributes];
_pendingRangeAttributes = nil;
_pendingRange.location = NSNotFound;
_initString = nil;
}
return _attrStr;
}
#pragma mark - Pending attribution
- (NSMutableDictionary *)_pendingRangeAttributes
{
// Lazy dictionary creation. Call this if you want to force initialization,
// otherwise just use the ivar.
if (_pendingRangeAttributes == nil) {
_pendingRangeAttributes = [[NSMutableDictionary alloc] init];
}
return _pendingRangeAttributes;
}
- (void)_applyPendingRangeAttributions
{
if (_attrStr == nil) {
// Trigger its creation if it doesn't exist.
[self _attributedString];
}
if (_pendingRangeAttributes.count == 0) {
return;
}
if (_pendingRange.location == NSNotFound) {
return;
}
if (_setRange) {
[[self _attributedString] setAttributes:_pendingRangeAttributes range:_pendingRange];
} else {
[[self _attributedString] addAttributes:_pendingRangeAttributes range:_pendingRange];
}
_pendingRangeAttributes = nil;
_pendingRange.location = NSNotFound;
}
#pragma mark - Editing
- (void)replaceCharactersInRange:(NSRange)range withString:(NSString *)str
{
[self _applyPendingRangeAttributions];
[[self _attributedString] replaceCharactersInRange:range withString:str];
}
- (void)replaceCharactersInRange:(NSRange)range withAttributedString:(NSAttributedString *)attrString
{
[self _applyPendingRangeAttributions];
[[self _attributedString] replaceCharactersInRange:range withAttributedString:attrString];
}
- (void)addAttribute:(NSString *)name value:(id)value range:(NSRange)range
{
if (_setRange) {
[self _applyPendingRangeAttributions];
_setRange = NO;
}
if (!NSEqualRanges(_pendingRange, range)) {
[self _applyPendingRangeAttributions];
_pendingRange = range;
}
NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes];
pendingAttributes[name] = value;
}
- (void)addAttributes:(NSDictionary *)attrs range:(NSRange)range
{
if (_setRange) {
[self _applyPendingRangeAttributions];
_setRange = NO;
}
if (!NSEqualRanges(_pendingRange, range)) {
[self _applyPendingRangeAttributions];
_pendingRange = range;
}
NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes];
[pendingAttributes addEntriesFromDictionary:attrs];
}
- (void)insertAttributedString:(NSAttributedString *)attrString atIndex:(NSUInteger)loc
{
[self _applyPendingRangeAttributions];
[[self _attributedString] insertAttributedString:attrString atIndex:loc];
}
- (void)appendAttributedString:(NSAttributedString *)attrString
{
[self _applyPendingRangeAttributions];
[[self _attributedString] appendAttributedString:attrString];
}
- (void)deleteCharactersInRange:(NSRange)range
{
[self _applyPendingRangeAttributions];
[[self _attributedString] deleteCharactersInRange:range];
}
- (void)setAttributedString:(NSAttributedString *)attrString
{
[self _applyPendingRangeAttributions];
[[self _attributedString] setAttributedString:attrString];
}
- (void)setAttributes:(NSDictionary *)attrs range:(NSRange)range
{
if (!_setRange) {
[self _applyPendingRangeAttributions];
_setRange = YES;
}
if (!NSEqualRanges(_pendingRange, range)) {
[self _applyPendingRangeAttributions];
_pendingRange = range;
}
NSMutableDictionary *pendingAttributes = [self _pendingRangeAttributes];
[pendingAttributes addEntriesFromDictionary:attrs];
}
- (void)removeAttribute:(NSString *)name range:(NSRange)range
{
// This call looks like the other set/add functions, but in order for this
// function to perform as advertised we MUST first add the attributes we
// currently have pending.
[self _applyPendingRangeAttributions];
[[self _attributedString] removeAttribute:name range:range];
}
#pragma mark - Output
- (NSMutableAttributedString *)composedAttributedString
{
if (_pendingRangeAttributes.count > 0) {
[self _applyPendingRangeAttributions];
}
return [self _attributedString];
}
#pragma mark - Forwarding
- (NSUInteger)length
{
// If we just want a length call, no need to lazily construct the attributed string
return _attrStr ? _attrStr.length : _initString.length;
}
- (NSDictionary *)attributesAtIndex:(NSUInteger)location effectiveRange:(NSRangePointer)range
{
return [[self _attributedString] attributesAtIndex:location effectiveRange:range];
}
- (NSString *)string
{
return _attrStr ? _attrStr.string : _initString;
}
- (NSMutableString *)mutableString
{
return [[self _attributedString] mutableString];
}
- (void)beginEditing
{
[[self _attributedString] beginEditing];
}
- (void)endEditing
{
[[self _attributedString] endEditing];
}
@end

View File

@ -1,66 +0,0 @@
//
// ASMutableElementMap.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
#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/ASIntegerMap.h>
NS_ASSUME_NONNULL_BEGIN
@class ASSection, ASCollectionElement, _ASHierarchyChangeSet;
/**
* This mutable version will be removed in the future. It's only here now to keep the diff small
* as we port data controller to use ASElementMap.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASMutableElementMap : NSObject <NSCopying>
- (instancetype)init __unavailable;
- (instancetype)initWithSections:(NSArray<ASSection *> *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements;
- (void)insertSection:(ASSection *)section atIndex:(NSInteger)index;
- (void)removeAllSections;
/// Only modifies the array of ASSection * objects
- (void)removeSectionsAtIndexes:(NSIndexSet *)indexes;
- (void)removeAllElements;
- (void)removeItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;
- (void)removeSectionsOfItems:(NSIndexSet *)itemSections;
- (void)removeSupplementaryElementsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths kind:(NSString *)kind;
- (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections;
- (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath;
/**
* Update the index paths for all supplementary elements to account for section-level
* deletes, moves, inserts. This must be called before adding new supplementary elements.
*
* This also deletes any supplementary elements in deleted sections.
*/
- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping;
@end
@interface ASElementMap (MutableCopying) <NSMutableCopying>
@end
NS_ASSUME_NONNULL_END
#endif

View File

@ -1,149 +0,0 @@
//
// ASMutableElementMap.mm
// 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
#import <AsyncDisplayKit/ASMutableElementMap.h>
#import <AsyncDisplayKit/ASCollectionElement.h>
#import <AsyncDisplayKit/ASDataController.h>
#import <AsyncDisplayKit/ASElementMap.h>
#import <AsyncDisplayKit/ASTwoDimensionalArrayUtils.h>
#import <AsyncDisplayKit/NSIndexSet+ASHelpers.h>
typedef NSMutableArray<NSMutableArray<ASCollectionElement *> *> ASMutableCollectionElementTwoDimensionalArray;
typedef NSMutableDictionary<NSString *, NSMutableDictionary<NSIndexPath *, ASCollectionElement *> *> ASMutableSupplementaryElementDictionary;
@implementation ASMutableElementMap {
ASMutableSupplementaryElementDictionary *_supplementaryElements;
NSMutableArray<ASSection *> *_sections;
ASMutableCollectionElementTwoDimensionalArray *_sectionsOfItems;
}
- (instancetype)initWithSections:(NSArray<ASSection *> *)sections items:(ASCollectionElementTwoDimensionalArray *)items supplementaryElements:(ASSupplementaryElementDictionary *)supplementaryElements
{
if (self = [super init]) {
_sections = [sections mutableCopy];
_sectionsOfItems = (ASMutableCollectionElementTwoDimensionalArray *)ASTwoDimensionalArrayDeepMutableCopy(items);
_supplementaryElements = [ASMutableElementMap deepMutableCopyOfElementsDictionary:supplementaryElements];
}
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
return [[ASElementMap alloc] initWithSections:_sections items:_sectionsOfItems supplementaryElements:_supplementaryElements];
}
- (void)removeAllSections
{
[_sections removeAllObjects];
}
- (void)insertSection:(ASSection *)section atIndex:(NSInteger)index
{
[_sections insertObject:section atIndex:index];
}
- (void)removeItemsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths
{
ASDeleteElementsInTwoDimensionalArrayAtIndexPaths(_sectionsOfItems, indexPaths);
}
- (void)removeSectionsAtIndexes:(NSIndexSet *)indexes
{
[_sections removeObjectsAtIndexes:indexes];
}
- (void)removeSupplementaryElementsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths kind:(NSString *)kind
{
[_supplementaryElements[kind] removeObjectsForKeys:indexPaths];
}
- (void)removeAllElements
{
[_sectionsOfItems removeAllObjects];
[_supplementaryElements removeAllObjects];
}
- (void)removeSectionsOfItems:(NSIndexSet *)itemSections
{
[_sectionsOfItems removeObjectsAtIndexes:itemSections];
}
- (void)insertEmptySectionsOfItemsAtIndexes:(NSIndexSet *)sections
{
[sections enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL * _Nonnull stop) {
[_sectionsOfItems insertObject:[[NSMutableArray alloc] init] atIndex:idx];
}];
}
- (void)insertElement:(ASCollectionElement *)element atIndexPath:(NSIndexPath *)indexPath
{
NSString *kind = element.supplementaryElementKind;
if (kind == nil) {
[_sectionsOfItems[indexPath.section] insertObject:element atIndex:indexPath.item];
} else {
NSMutableDictionary *supplementariesForKind = _supplementaryElements[kind];
if (supplementariesForKind == nil) {
supplementariesForKind = [[NSMutableDictionary alloc] init];
_supplementaryElements[kind] = supplementariesForKind;
}
supplementariesForKind[indexPath] = element;
}
}
- (void)migrateSupplementaryElementsWithSectionMapping:(ASIntegerMap *)mapping
{
// Fast-path, no section changes.
if (mapping == ASIntegerMap.identityMap) {
return;
}
// For each element kind,
[_supplementaryElements enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSMutableDictionary<NSIndexPath *,ASCollectionElement *> * _Nonnull supps, BOOL * _Nonnull stop) {
// For each index path of that kind, move entries into a new dictionary.
// Note: it's tempting to update the dictionary in-place but because of the likely collision between old and new index paths,
// subtle bugs are possible. Note that this process is rare (only on section-level updates),
// that this work is done off-main, and that the typical supplementary element use case is just 1-per-section (header).
NSMutableDictionary *newSupps = [[NSMutableDictionary alloc] init];
[supps enumerateKeysAndObjectsUsingBlock:^(NSIndexPath * _Nonnull oldIndexPath, ASCollectionElement * _Nonnull obj, BOOL * _Nonnull stop) {
NSInteger oldSection = oldIndexPath.section;
NSInteger newSection = [mapping integerForKey:oldSection];
if (oldSection == newSection) {
// Index path stayed the same, just copy it over.
newSupps[oldIndexPath] = obj;
} else if (newSection != NSNotFound) {
// Section index changed, move it.
NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:oldIndexPath.item inSection:newSection];
newSupps[newIndexPath] = obj;
}
}];
[supps setDictionary:newSupps];
}];
}
#pragma mark - Helpers
+ (ASMutableSupplementaryElementDictionary *)deepMutableCopyOfElementsDictionary:(ASSupplementaryElementDictionary *)originalDict
{
NSMutableDictionary *deepCopy = [[NSMutableDictionary alloc] initWithCapacity:originalDict.count];
[originalDict enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSDictionary<NSIndexPath *,ASCollectionElement *> * _Nonnull obj, BOOL * _Nonnull stop) {
deepCopy[key] = [obj mutableCopy];
}];
return deepCopy;
}
@end
#endif

View File

@ -1,59 +0,0 @@
//
// ASNodeController+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 <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h> // for ASInterfaceState protocol
/* ASNodeController is currently beta and open to change in the future */
@interface ASNodeController<__covariant DisplayNodeType : ASDisplayNode *>
: NSObject <ASInterfaceStateDelegate, ASLocking>
@property (nonatomic, strong /* may be weak! */) DisplayNodeType node;
// Until an ASNodeController can be provided in place of an ASCellNode, some apps may prefer to have
// nodes keep their controllers alive (and a weak reference from controller to node)
@property (nonatomic) BOOL shouldInvertStrongReference;
- (void)loadNode;
// for descriptions see <ASInterfaceState> definition
- (void)nodeDidLoad ASDISPLAYNODE_REQUIRES_SUPER;
- (void)nodeDidLayout ASDISPLAYNODE_REQUIRES_SUPER;
// This is only called during Yoga-driven layouts.
- (void)nodeWillCalculateLayout:(ASSizeRange)constrainedSize ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didEnterVisibleState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didExitVisibleState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didEnterDisplayState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didExitDisplayState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didEnterPreloadState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)didExitPreloadState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)interfaceStateDidChange:(ASInterfaceState)newState
fromState:(ASInterfaceState)oldState ASDISPLAYNODE_REQUIRES_SUPER;
- (void)hierarchyDisplayDidFinish ASDISPLAYNODE_REQUIRES_SUPER;
/**
* @discussion Attempts (via ASLockSequence, a backing-off spinlock similar to
* std::lock()) to lock both the node and its ASNodeController, if one exists.
*/
- (ASLockSet)lockPair;
@end
@interface ASDisplayNode (ASNodeController)
@property(nonatomic, readonly) ASNodeController *nodeController;
@end

Some files were not shown because too many files have changed in this diff Show More