Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve separation of code for layout method types #1305

Merged
merged 2 commits into from
Jan 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,9 @@
9C8898BD1C738BB800D6B02E /* ASTextKitFontSizeAdjuster.h in Headers */ = {isa = PBXBuildFile; fileRef = A32FEDD31C501B6A004F642A /* ASTextKitFontSizeAdjuster.h */; settings = {ATTRIBUTES = (Private, ); }; };
9CC606651D24DF9E006581A0 /* NSIndexSet+ASHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.mm */; };
9CDC18CD1B910E12004965E2 /* ASLayoutElementPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = 9CDC18CB1B910E12004965E2 /* ASLayoutElementPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };
9D9AA56921E23EE200172C09 /* ASDisplayNode+LayoutSpec.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9D9AA56721E23EE200172C09 /* ASDisplayNode+LayoutSpec.mm */; };
9D9AA56B21E254B800172C09 /* ASDisplayNode+Yoga.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9AA56A21E254B800172C09 /* ASDisplayNode+Yoga.h */; };
9D9AA56D21E2568500172C09 /* ASDisplayNode+LayoutSpec.h in Headers */ = {isa = PBXBuildFile; fileRef = 9D9AA56C21E2568500172C09 /* ASDisplayNode+LayoutSpec.h */; settings = {ATTRIBUTES = (Public, ); }; };
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.mm */; };
9F98C0261DBE29E000476D92 /* ASControlTargetAction.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9F98C0241DBDF2A300476D92 /* ASControlTargetAction.mm */; };
9F98C0271DBE29FC00476D92 /* ASControlTargetAction.h in Headers */ = {isa = PBXBuildFile; fileRef = 9F98C0231DBDF2A300476D92 /* ASControlTargetAction.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -774,6 +777,9 @@
9CDC18CB1B910E12004965E2 /* ASLayoutElementPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutElementPrivate.h; sourceTree = "<group>"; };
9CFFC6BF1CCAC73C006A6476 /* ASViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASViewController.mm; sourceTree = "<group>"; };
9CFFC6C11CCAC768006A6476 /* ASTableNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTableNode.mm; sourceTree = "<group>"; };
9D9AA56721E23EE200172C09 /* ASDisplayNode+LayoutSpec.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = "ASDisplayNode+LayoutSpec.mm"; sourceTree = "<group>"; };
9D9AA56A21E254B800172C09 /* ASDisplayNode+Yoga.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Yoga.h"; sourceTree = "<group>"; };
9D9AA56C21E2568500172C09 /* ASDisplayNode+LayoutSpec.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+LayoutSpec.h"; sourceTree = "<group>"; };
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASCollectionViewTests.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
9F98C0231DBDF2A300476D92 /* ASControlTargetAction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASControlTargetAction.h; sourceTree = "<group>"; };
9F98C0241DBDF2A300476D92 /* ASControlTargetAction.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASControlTargetAction.mm; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1209,6 +1215,9 @@
683F563620E409D600CEB7A3 /* ASDisplayNode+InterfaceState.h */,
69BCE3D71EC6513B007DCCAD /* ASDisplayNode+Layout.mm */,
058D09DA195D050800B7D73C /* ASDisplayNode+Subclasses.h */,
9D9AA56C21E2568500172C09 /* ASDisplayNode+LayoutSpec.h */,
9D9AA56721E23EE200172C09 /* ASDisplayNode+LayoutSpec.mm */,
9D9AA56A21E254B800172C09 /* ASDisplayNode+Yoga.h */,
90FC784E1E4BFE1B00383C5A /* ASDisplayNode+Yoga.mm */,
058D09DB195D050800B7D73C /* ASDisplayNodeExtras.h */,
058D09DC195D050800B7D73C /* ASDisplayNodeExtras.mm */,
Expand Down Expand Up @@ -1887,6 +1896,7 @@
CCBDDD0520C62A2D00CBA922 /* ASMainThreadDeallocation.h in Headers */,
CCAA0B7F206ADBF30057B336 /* ASRecursiveUnfairLock.h in Headers */,
E5E281741E71C833006B67C2 /* ASCollectionLayoutState.h in Headers */,
9D9AA56D21E2568500172C09 /* ASDisplayNode+LayoutSpec.h in Headers */,
E5B077FF1E69F4EB00C24B5B /* ASElementMap.h in Headers */,
CCCCCCE31EC3EF060087FE10 /* NSParagraphStyle+ASText.h in Headers */,
E58E9E441E941D74004CFC59 /* ASCollectionLayoutContext.h in Headers */,
Expand Down Expand Up @@ -2018,6 +2028,7 @@
B35062531B010EFD0018CF92 /* ASImageNode+CGExtras.h in Headers */,
E58E9E491E941DA5004CFC59 /* ASCollectionLayout.h in Headers */,
254C6B7F1BF94DF4003EC431 /* ASTextKitTruncating.h in Headers */,
9D9AA56B21E254B800172C09 /* ASDisplayNode+Yoga.h in Headers */,
CC58AA4B1E398E1D002C8CB4 /* ASBlockTypes.h in Headers */,
CCA282BC1E9EABDD0037E8B7 /* ASTipProvider.h in Headers */,
6977965F1D8AC8D3007E93D7 /* ASLayoutSpec+Subclasses.h in Headers */,
Expand Down Expand Up @@ -2395,6 +2406,7 @@
CCDC9B4E200991D10063C1F8 /* ASGraphicsContext.mm in Sources */,
CCCCCCD81EC3EF060087FE10 /* ASTextInput.mm in Sources */,
34EFC7621B701CA400AD841F /* ASBackgroundLayoutSpec.mm in Sources */,
9D9AA56921E23EE200172C09 /* ASDisplayNode+LayoutSpec.mm in Sources */,
DE8BEAC41C2DF3FC00D57C12 /* ASDelegateProxy.mm in Sources */,
B35062141B010EFD0018CF92 /* ASBasicImageDownloader.mm in Sources */,
B35062161B010EFD0018CF92 /* ASBatchContext.mm in Sources */,
Expand Down
1 change: 0 additions & 1 deletion Source/ASCellNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#import <AsyncDisplayKit/ASTableView+Undeprecated.h>
#import <AsyncDisplayKit/_ASDisplayView.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import <AsyncDisplayKit/ASTextNode.h>
#import <AsyncDisplayKit/ASCollectionNode.h>
#import <AsyncDisplayKit/ASTableNode.h>
Expand Down
60 changes: 1 addition & 59 deletions Source/ASDisplayNode+Beta.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#if YOGA
#import YOGA_HEADER_PATH
#import <AsyncDisplayKit/ASYogaUtilities.h>
#import <AsyncDisplayKit/ASDisplayNode+Yoga.h>
#endif

NS_ASSUME_NONNULL_BEGIN
Expand Down Expand Up @@ -180,63 +181,4 @@ typedef struct {

@end

#pragma mark - Yoga Layout Support

#if YOGA

AS_EXTERN void ASDisplayNodePerformBlockOnEveryYogaChild(ASDisplayNode * _Nullable node, void(^block)(ASDisplayNode *node));

@interface ASDisplayNode (Yoga)

@property (copy) NSArray *yogaChildren;

- (void)addYogaChild:(ASDisplayNode *)child;
- (void)removeYogaChild:(ASDisplayNode *)child;
- (void)insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index;

- (void)semanticContentAttributeDidChange:(UISemanticContentAttribute)attribute;

@property BOOL yogaLayoutInProgress;
// TODO: Make this atomic (lock).
@property (nullable, nonatomic) ASLayout *yogaCalculatedLayout;

// Will walk up the Yoga tree and returns the root node
- (ASDisplayNode *)yogaRoot;

// These methods are intended to be used internally to Texture, and should not be called directly.
- (BOOL)shouldHaveYogaMeasureFunc;
- (void)invalidateCalculatedYogaLayout;
- (void)calculateLayoutFromYogaRoot:(ASSizeRange)rootConstrainedSize;

@end

@interface ASDisplayNode (YogaDebugging)

- (NSString *)yogaTreeDescription;

@end

@interface ASLayoutElementStyle (Yoga)

- (YGNodeRef)yogaNodeCreateIfNeeded;
- (void)destroyYogaNode;

@property (readonly) YGNodeRef yogaNode;

@property ASStackLayoutDirection flexDirection;
@property YGDirection direction;
@property ASStackLayoutJustifyContent justifyContent;
@property ASStackLayoutAlignItems alignItems;
@property YGPositionType positionType;
@property ASEdgeInsets position;
@property ASEdgeInsets margin;
@property ASEdgeInsets padding;
@property ASEdgeInsets border;
@property CGFloat aspectRatio;
@property YGWrap flexWrap;

@end

#endif

NS_ASSUME_NONNULL_END
21 changes: 11 additions & 10 deletions Source/ASDisplayNode+Layout.mm
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//

#import <AsyncDisplayKit/ASAvailability.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import <AsyncDisplayKit/ASDisplayNodeInternal.h>
#import <AsyncDisplayKit/ASDisplayNode+FrameworkPrivate.h>
Expand Down Expand Up @@ -143,19 +144,19 @@ - (NSString *)asciiArtName

@implementation ASDisplayNode (ASLayout)

- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
- (ASLayoutEngineType)layoutEngineType
{
// For now there should never be an override of layoutSpecThatFits: and a layoutSpecBlock together.
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits),
@"Nodes with a .layoutSpecBlock must not also implement -layoutSpecThatFits:");
#if YOGA
ASDN::MutexLocker l(__instanceLock__);
_layoutSpecBlock = layoutSpecBlock;
}
YGNodeRef yogaNode = _style.yogaNode;
BOOL hasYogaParent = (_yogaParent != nil);
BOOL hasYogaChildren = (_yogaChildren.count > 0);
if (yogaNode != NULL && (hasYogaParent || hasYogaChildren)) {
return ASLayoutEngineTypeYoga;
}
#endif

- (ASLayoutSpecBlock)layoutSpecBlock
{
ASDN::MutexLocker l(__instanceLock__);
return _layoutSpecBlock;
return ASLayoutEngineTypeLayoutSpec;
}

- (ASLayout *)calculatedLayout
Expand Down
66 changes: 66 additions & 0 deletions Source/ASDisplayNode+LayoutSpec.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// ASDisplayNode+LayoutSpec.h
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//

#import <AsyncDisplayKit/ASDisplayNode.h>
#import <AsyncDisplayKit/ASDimension.h>

@class ASLayout;

NS_ASSUME_NONNULL_BEGIN

@interface ASDisplayNode (ASLayoutSpec)

/**
* @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
* implement layoutSpecThatFits:
*
* @return A block that takes a constrainedSize ASSizeRange argument, and must return an ASLayoutSpec that includes all
* of the subnodes to position in the layout. This input-output relationship is identical to the subclass override
* method -layoutSpecThatFits:
*
* @warning Subclasses that implement -layoutSpecThatFits: must not also use .layoutSpecBlock. Doing so will trigger
* an exception. A future version of the framework may support using both, calling them serially, with the
* .layoutSpecBlock superseding any values set by the method override.
*
* @code ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {};
*/
@property (nullable) ASLayoutSpecBlock layoutSpecBlock;

@end

// These methods are intended to be used internally to Texture, and should not be called directly.
@interface ASDisplayNode (ASLayoutSpecPrivate)

/// For internal usage only
- (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize;

@end

@interface ASDisplayNode (ASLayoutSpecSubclasses)

/**
* @abstract Return a layout spec that describes the layout of the receiver and its children.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @discussion Subclasses that override should expect this method to be called on a non-main thread. The returned layout spec
* is used to calculate an ASLayout and cached by ASDisplayNode for quick access during -layout. Other expensive work that needs to
* be done before display can be performed here, and using ivars to cache any valuable intermediate results is
* encouraged.
*
* @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: instead.
*
* @warning Subclasses that implement -layoutSpecThatFits: must not use .layoutSpecBlock. Doing so will trigger an
* exception. A future version of the framework may support using both, calling them serially, with the .layoutSpecBlock
* superseding any values set by the method override.
*/
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;

@end

NS_ASSUME_NONNULL_END
146 changes: 146 additions & 0 deletions Source/ASDisplayNode+LayoutSpec.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//
// ASDisplayNode+LayoutSpec.mm
// Texture
//
// Copyright (c) Pinterest, Inc. All rights reserved.
// Licensed under Apache 2.0: http://www.apache.org/licenses/LICENSE-2.0
//

#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASAvailability.h>

#import <AsyncDisplayKit/_ASScopeTimer.h>
#import <AsyncDisplayKit/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 <AsyncDisplayKit/ASThread.h>


@implementation ASDisplayNode (ASLayoutSpec)

- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
{
// For now there should never be an override of layoutSpecThatFits: and a layoutSpecBlock together.
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits),
@"Nodes with a .layoutSpecBlock must not also implement -layoutSpecThatFits:");
ASDN::MutexLocker l(__instanceLock__);
_layoutSpecBlock = layoutSpecBlock;
}

- (ASLayoutSpecBlock)layoutSpecBlock
{
ASDN::MutexLocker l(__instanceLock__);
return _layoutSpecBlock;
}

- (ASLayout *)calculateLayoutLayoutSpec:(ASSizeRange)constrainedSize
{
ASDN::UniqueLock l(__instanceLock__);

// Manual size calculation via calculateSizeThatFits:
if (_layoutSpecBlock == NULL && (_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits) == 0) {
CGSize size = [self calculateSizeThatFits:constrainedSize.max];
ASDisplayNodeLogEvent(self, @"calculatedSize: %@", NSStringFromCGSize(size));
return [ASLayout layoutWithLayoutElement:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil];
}

// Size calcualtion with layout elements
BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;
if (measureLayoutSpec) {
_layoutSpecNumberOfPasses++;
}

// Get layout element from the node
id<ASLayoutElement> layoutElement = [self _locked_layoutElementThatFits:constrainedSize];
#if ASEnableVerboseLogging
for (NSString *asciiLine in [[layoutElement asciiArtString] componentsSeparatedByString:@"\n"]) {
as_log_verbose(ASLayoutLog(), "%@", asciiLine);
}
#endif


// Certain properties are necessary to set on an element of type ASLayoutSpec
if (layoutElement.layoutElementType == ASLayoutElementTypeLayoutSpec) {
ASLayoutSpec *layoutSpec = (ASLayoutSpec *)layoutElement;

#if AS_DEDUPE_LAYOUT_SPEC_TREE
NSHashTable *duplicateElements = [layoutSpec findDuplicatedElementsInSubtree];
if (duplicateElements.count > 0) {
ASDisplayNodeFailAssert(@"Node %@ returned a layout spec that contains the same elements in multiple positions. Elements: %@", self, duplicateElements);
// Use an empty layout spec to avoid crashes
layoutSpec = [[ASLayoutSpec alloc] init];
}
#endif

ASDisplayNodeAssert(layoutSpec.isMutable, @"Node %@ returned layout spec %@ that has already been used. Layout specs should always be regenerated.", self, layoutSpec);

layoutSpec.isMutable = NO;
}

// Manually propagate the trait collection here so that any layoutSpec children of layoutSpec will get a traitCollection
{
ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
ASTraitCollectionPropagateDown(layoutElement, self.primitiveTraitCollection);
}

BOOL measureLayoutComputation = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutComputation;
if (measureLayoutComputation) {
_layoutComputationNumberOfPasses++;
}

// Layout element layout creation
ASLayout *layout = ({
ASDN::SumScopeTimer t(_layoutComputationTotalTime, measureLayoutComputation);
[layoutElement layoutThatFits:constrainedSize];
});
ASDisplayNodeAssertNotNil(layout, @"[ASLayoutElement layoutThatFits:] should never return nil! %@, %@", self, layout);

// Make sure layoutElementObject of the root layout is `self`, so that the flattened layout will be structurally correct.
BOOL isFinalLayoutElement = (layout.layoutElement != self);
if (isFinalLayoutElement) {
layout.position = CGPointZero;
layout = [ASLayout layoutWithLayoutElement:self size:layout.size sublayouts:@[layout]];
}
ASDisplayNodeLogEvent(self, @"computedLayout: %@", layout);

// PR #1157: Reduces accuracy of _unflattenedLayout for debugging/Weaver
if ([ASDisplayNode shouldStoreUnflattenedLayouts]) {
_unflattenedLayout = layout;
}
layout = [layout filteredNodeLayoutTree];

return layout;
}

- (id<ASLayoutElement>)_locked_layoutElementThatFits:(ASSizeRange)constrainedSize
{
ASAssertLocked(__instanceLock__);

BOOL measureLayoutSpec = _measurementOptions & ASDisplayNodePerformanceMeasurementOptionLayoutSpec;

if (_layoutSpecBlock != NULL) {
return ({
ASDN::MutexLocker l(__instanceLock__);
ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
_layoutSpecBlock(self, constrainedSize);
});
} else {
return ({
ASDN::SumScopeTimer t(_layoutSpecTotalTime, measureLayoutSpec);
[self layoutSpecThatFits:constrainedSize];
});
}
}

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
__ASDisplayNodeCheckForLayoutMethodOverrides;

ASDisplayNodeAssert(NO, @"-[ASDisplayNode layoutSpecThatFits:] should never return an empty value. One way this is caused is by calling -[super layoutSpecThatFits:] which is not currently supported.");
return [[ASLayoutSpec alloc] init];
}

@end
Loading