Moved ASLayoutable* properties into ASLayoutOptions class

This commit is contained in:
rcancro 2015-08-28 09:36:22 -07:00
parent 32e2f7f1ad
commit afade854af
26 changed files with 373 additions and 189 deletions

View File

@ -231,6 +231,10 @@
9C49C3701B853961000B0DD5 /* ASStackLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C6BB3B21B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C6BB3B31B8CC9C200F13F52 /* ASStaticLayoutable.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; };
9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */; };
9F06E5CD1B4CAF4200F015D8 /* ASCollectionViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */; };
AC21EC101B3D0BF600C8B19A /* ASStackLayoutDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */; settings = {ATTRIBUTES = (Public, ); }; };
AC3C4A511A1139C100143C57 /* ASCollectionView.h in Headers */ = {isa = PBXBuildFile; fileRef = AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */; settings = {ATTRIBUTES = (Public, ); }; };
@ -577,6 +581,8 @@
9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASBaselineLayoutSpec.mm; path = AsyncDisplayKit/Layout/ASBaselineLayoutSpec.mm; sourceTree = "<group>"; };
9C49C36E1B853957000B0DD5 /* ASStackLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutable.h; path = AsyncDisplayKit/Layout/ASStackLayoutable.h; sourceTree = "<group>"; };
9C6BB3B01B8CC9C200F13F52 /* ASStaticLayoutable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStaticLayoutable.h; path = AsyncDisplayKit/Layout/ASStaticLayoutable.h; sourceTree = "<group>"; };
9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutOptions.h; path = AsyncDisplayKit/Layout/ASLayoutOptions.h; sourceTree = "<group>"; };
9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ASLayoutOptions.m; path = AsyncDisplayKit/Layout/ASLayoutOptions.m; sourceTree = "<group>"; };
9F06E5CC1B4CAF4200F015D8 /* ASCollectionViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASCollectionViewTests.m; sourceTree = "<group>"; };
AC21EC0F1B3D0BF600C8B19A /* ASStackLayoutDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASStackLayoutDefines.h; path = AsyncDisplayKit/Layout/ASStackLayoutDefines.h; sourceTree = "<group>"; };
AC3C4A4F1A1139C100143C57 /* ASCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASCollectionView.h; sourceTree = "<group>"; };
@ -993,6 +999,8 @@
ACF6ED191B17843500DA7C62 /* ASStaticLayoutSpec.mm */,
9C3061041B857EC400D0530B /* ASBaselineLayoutSpec.h */,
9C3061051B857EC400D0530B /* ASBaselineLayoutSpec.mm */,
9C5FA34F1B8F6ADF00A62714 /* ASLayoutOptions.h */,
9C5FA3501B8F6ADF00A62714 /* ASLayoutOptions.m */,
);
name = Layout;
path = ..;
@ -1060,6 +1068,7 @@
058D0A49195D05CB00B7D73C /* ASControlNode+Subclasses.h in Headers */,
058D0A4A195D05CB00B7D73C /* ASDisplayNode.h in Headers */,
1950C4491A3BB5C1005C8279 /* ASEqualityHelpers.h in Headers */,
9C5FA3511B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */,
058D0A4B195D05CB00B7D73C /* ASDisplayNode.mm in Headers */,
430E7C8F1B4C23F100697A4C /* ASIndexPath.h in Headers */,
058D0A4C195D05CB00B7D73C /* ASDisplayNode+Subclasses.h in Headers */,
@ -1191,6 +1200,7 @@
B35062391B010EFD0018CF92 /* ASThread.h in Headers */,
B35062131B010EFD0018CF92 /* ASBasicImageDownloader.h in Headers */,
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
9C5FA3521B8F6ADF00A62714 /* ASLayoutOptions.h in Headers */,
34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */,
B35062221B010EFD0018CF92 /* ASMultidimensionalArrayUtils.h in Headers */,
B350625B1B010F070018CF92 /* ASEqualityHelpers.h in Headers */,
@ -1451,6 +1461,7 @@
ACF6ED2E1B17843500DA7C62 /* ASRatioLayoutSpec.mm in Sources */,
058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */,
ACF6ED321B17843500DA7C62 /* ASStaticLayoutSpec.mm in Sources */,
9C5FA3531B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */,
ACF6ED2C1B17843500DA7C62 /* ASOverlayLayoutSpec.mm in Sources */,
058D0A2C195D050800B7D73C /* ASSentinel.m in Sources */,
205F0E221B376416007741D0 /* CGRect+ASConvenience.m in Sources */,
@ -1577,6 +1588,7 @@
B350621C1B010EFD0018CF92 /* ASFlowLayoutController.mm in Sources */,
B35062231B010EFD0018CF92 /* ASMultidimensionalArrayUtils.mm in Sources */,
509E68641B3AEDB7009B9150 /* ASCollectionViewLayoutController.mm in Sources */,
9C5FA3541B8F6ADF00A62714 /* ASLayoutOptions.m in Sources */,
B350621E1B010EFD0018CF92 /* ASHighlightOverlayLayer.mm in Sources */,
B35062271B010EFD0018CF92 /* ASRangeController.mm in Sources */,
B35061F91B010EFD0018CF92 /* ASControlNode.m in Sources */,

View File

@ -13,7 +13,7 @@
#import <AsyncDisplayKit/ASDealloc2MainObject.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutable.h>
#import <AsyncDisplayKit/ASLayoutable.h>
/**
* UIView creation block. Used to create the backing view of a new display node.
@ -40,7 +40,7 @@ typedef CALayer *(^ASDisplayNodeLayerBlock)();
*
*/
@interface ASDisplayNode : ASDealloc2MainObject <ASStackLayoutable>
@interface ASDisplayNode : ASDealloc2MainObject <ASLayoutable>
/** @name Initializing a node object */

View File

@ -41,12 +41,6 @@
@implementation ASDisplayNode
@synthesize spacingBefore = _spacingBefore;
@synthesize spacingAfter = _spacingAfter;
@synthesize flexGrow = _flexGrow;
@synthesize flexShrink = _flexShrink;
@synthesize flexBasis = _flexBasis;
@synthesize alignSelf = _alignSelf;
@synthesize preferredFrameSize = _preferredFrameSize;
BOOL ASDisplayNodeSubclassOverridesSelector(Class subclass, SEL selector)
@ -155,7 +149,6 @@ void ASDisplayNodeRespectThreadAffinityOfNode(ASDisplayNode *node, void (^block)
}
_methodOverrides = overrides;
_flexBasis = ASRelativeDimensionUnconstrained;
_preferredFrameSize = CGSizeZero;
}

View File

@ -7,7 +7,6 @@
*/
#import <AsyncDisplayKit/ASControlNode.h>
#import <AsyncDisplayKit/ASBaselineLayoutable.h>
@protocol ASTextNodeDelegate;
@ -30,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASTextNodeHighlightStyle) {
@abstract Draws interactive rich text.
@discussion Backed by TextKit.
*/
@interface ASTextNode : ASControlNode <ASBaselineLayoutable>
@interface ASTextNode : ASControlNode
/**
@abstract The attributed string to show.

View File

@ -17,7 +17,6 @@
#import <AsyncDisplayKit/ASTextNodeTextKitHelpers.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
#import "ASInternalHelpers.h"
#import "ASTextNodeRenderer.h"
#import "ASTextNodeShadower.h"
@ -108,9 +107,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
UILongPressGestureRecognizer *_longPressGestureRecognizer;
}
@synthesize ascender = _ascender;
@synthesize descender = _descender;
#pragma mark - NSObject
- (instancetype)init
@ -359,9 +355,6 @@ ASDISPLAYNODE_INLINE CGFloat ceilPixelValue(CGFloat f)
self.isAccessibilityElement = YES;
}
});
_ascender = round([[attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale();
_descender = round([[attributedString attribute:NSFontAttributeName atIndex:attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale();
}
#pragma mark - Text Layout

View File

@ -9,7 +9,7 @@
*/
#import <AsyncDisplayKit/ASStackLayoutSpec.h>
#import <AsyncDisplayKit/ASBaselineLayoutable.h>
#import <AsyncDisplayKit/ASLayoutable.h>
typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) {
/** No baseline alignment. This is only valid for a vertical stack */
@ -29,7 +29,7 @@ typedef NS_ENUM(NSUInteger, ASBaselineLayoutBaselineAlignment) {
If the spec is created with a vertical direction, a child's vertical spacing will be measured from its
baseline instead of from the child's bounding box.
*/
@interface ASBaselineLayoutSpec : ASLayoutSpec <ASBaselineLayoutable>
@interface ASBaselineLayoutSpec : ASLayoutSpec
/** Specifies the direction children are stacked in. */
@property (nonatomic, assign) ASStackLayoutDirection direction;

View File

@ -9,7 +9,6 @@
*/
#import "ASBaselineLayoutSpec.h"
#import "ASStackLayoutable.h"
#import <numeric>
#import <vector>
@ -26,12 +25,6 @@
@implementation ASBaselineLayoutSpec
{
ASDN::RecursiveMutex _propertyLock;
}
@synthesize ascender = _ascender;
@synthesize descender = _descender;
- (instancetype)initWithDirection:(ASStackLayoutDirection)direction spacing:(CGFloat)spacing baselineAlignment:(ASBaselineLayoutBaselineAlignment)baselineAlignment justifyContent:(ASStackLayoutJustifyContent)justifyContent alignItems:(ASStackLayoutAlignItems)alignItems children:(NSArray *)children
{
@ -61,8 +54,8 @@
ASStackLayoutSpecStyle stackStyle = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems};
ASBaselineLayoutSpecStyle style = { .baselineAlignment = _baselineAlignment, .stackLayoutStyle = stackStyle };
std::vector<id<ASStackLayoutable>> stackChildren = std::vector<id<ASStackLayoutable>>();
for (id<ASStackLayoutable> child in self.children) {
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
for (id<ASLayoutable> child in self.children) {
stackChildren.push_back(child);
}
@ -74,25 +67,11 @@
NSArray *sublayouts = [NSArray arrayWithObjects:&baselinePositionedLayout.sublayouts[0] count:baselinePositionedLayout.sublayouts.size()];
ASDN::MutexLocker l(_propertyLock);
_ascender = baselinePositionedLayout.ascender;
_descender = baselinePositionedLayout.descender;
return [ASLayout layoutWithLayoutableObject:self
size:ASSizeRangeClamp(constrainedSize, finalSize)
sublayouts:sublayouts];
}
- (void)setChildren:(NSArray *)children
{
[super setChildren:children];
#if DEBUG
for (id<ASBaselineLayoutable> child in children) {
NSAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASBaselineLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASBaselineLayoutable)]), @"child must conform to ASBaselineLayoutable");
}
#endif
}
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
{
ASDisplayNodeAssert(NO, @"ASBaselineLayoutSpec only supports setChildren");

View File

@ -6,9 +6,9 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "ASStackLayoutable.h"
#import <UIKit/UIKit.h>
@protocol ASBaselineLayoutable <ASStackLayoutable>
@protocol ASBaselineLayoutable
/**
* @abstract The distance from the top of the layoutable object to its baseline

View File

@ -0,0 +1,49 @@
//
// ASLayoutOptions.h
// AsyncDisplayKit
//
// Created by Ricky Cancro on 8/27/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol ASLayoutable;
#import <AsyncDisplayKit/ASBaselineLayoutable.h>
#import <AsyncDisplayKit/ASStackLayoutable.h>
#import <AsyncDisplayKit/ASStaticLayoutable.h>
@interface ASLayoutOptions : NSObject <ASBaselineLayoutable, ASStackLayoutable, ASStaticLayoutable, NSCopying>
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable;
- (void)setValuesFromLayoutable:(id<ASLayoutable>)layoutable;
@property (nonatomic, assign) BOOL isMutable;
#if DEBUG
@property (nonatomic, assign) NSUInteger changeMonitor;
#endif
#pragma mark - ASStackLayoutable
@property (nonatomic, readwrite) CGFloat spacingBefore;
@property (nonatomic, readwrite) CGFloat spacingAfter;
@property (nonatomic, readwrite) BOOL flexGrow;
@property (nonatomic, readwrite) BOOL flexShrink;
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
@property (nonatomic, readwrite) ASStackLayoutAlignSelf alignSelf;
#pragma mark - ASBaselineLayoutable
@property (nonatomic, readwrite) CGFloat ascender;
@property (nonatomic, readwrite) CGFloat descender;
#pragma mark - ASStaticLayoutable
@property (nonatomic, readwrite) ASRelativeSizeRange sizeRange;
@property (nonatomic, readwrite) CGPoint position;
- (void)setupDefaults;
@end

View File

@ -0,0 +1,120 @@
//
// ASLayoutOptions.m
// AsyncDisplayKit
//
// Created by Ricky Cancro on 8/27/15.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "ASLayoutOptions.h"
#import <AsyncDisplayKit/ASAssert.h>
#import <AsyncDisplayKit/ASTextNode.h>
#import "ASInternalHelpers.h"
#import <objc/runtime.h>
@implementation ASLayoutOptions
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable;
{
self = [super init];
if (self) {
[self setupDefaults];
[self setValuesFromLayoutable:layoutable];
#if DEBUG
[self addObserver:self forKeyPath:@"changeMonitor"
options:NSKeyValueObservingOptionNew
context:nil];
#endif
}
return self;
}
#if DEBUG
+ (NSSet *)keyPathsForValuesAffectingChangeMonitor
{
NSMutableSet *keys = [NSMutableSet set];
unsigned int count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
for (size_t i = 0; i < count; ++i) {
NSString *property = [NSString stringWithCString:property_getName(properties[i]) encoding:NSASCIIStringEncoding];
if ([property isEqualToString: @"observableSelf"] == NO) {
[keys addObject: property];
}
}
free(properties);
return keys;
}
#endif
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
#if DEBUG
if ([keyPath isEqualToString:@"changeMonitor"]) {
ASDisplayNodeAssert(self.isMutable, @"You cannot alter this class once it is marked as immutable");
} else
#endif
{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark - NSCopying
- (id)copyWithZone:(NSZone *)zone
{
ASLayoutOptions *copy = [[[self class] alloc] init];
copy.flexBasis = self.flexBasis;
copy.spacingAfter = self.spacingAfter;
copy.spacingBefore = self.spacingBefore;
copy.flexGrow = self.flexGrow;
copy.flexShrink = self.flexShrink;
copy.ascender = self.ascender;
copy.descender = self.descender;
copy.sizeRange = self.sizeRange;
copy.position = self.position;
return copy;
}
#pragma mark - Defaults
- (void)setupDefaults
{
_flexBasis = ASRelativeDimensionUnconstrained;
_spacingBefore = 0;
_spacingAfter = 0;
_flexGrow = NO;
_flexShrink = NO;
_alignSelf = ASStackLayoutAlignSelfAuto;
_ascender = 0;
_descender = 0;
_sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero));
_position = CGPointZero;
}
// Do this here instead of in Node/Spec subclasses so that custom specs can set default values
- (void)setValuesFromLayoutable:(id<ASLayoutable>)layoutable
{
if ([layoutable isKindOfClass:[ASTextNode class]]) {
ASTextNode *textNode = (ASTextNode *)layoutable;
self.ascender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:0 effectiveRange:NULL] ascender] * ASScreenScale())/ASScreenScale();
self.descender = round([[textNode.attributedString attribute:NSFontAttributeName atIndex:textNode.attributedString.length - 1 effectiveRange:NULL] descender] * ASScreenScale())/ASScreenScale();
}
if ([layoutable isKindOfClass:[ASDisplayNode class]]) {
ASDisplayNode *displayNode = (ASDisplayNode *)layoutable;
self.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize), ASRelativeSizeMakeWithCGSize(displayNode.preferredFrameSize));
self.position = displayNode.frame.origin;
}
}
@end

View File

@ -8,10 +8,10 @@
*
*/
#import <AsyncDisplayKit/ASStackLayoutable.h>
#import <AsyncDisplayKit/ASLayoutable.h>
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
@interface ASLayoutSpec : NSObject <ASStackLayoutable>
@interface ASLayoutSpec : NSObject <ASLayoutable>
/**
Creation of a layout spec should only happen by a user in layoutSpecThatFits:. During that method, a
@ -23,12 +23,15 @@
- (instancetype)init;
- (void)setChild:(id<ASLayoutable>)child;
- (id<ASLayoutable>)child;
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier;
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier;
- (void)setChildren:(NSArray *)children;
- (id<ASLayoutable>)child;
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier;
- (NSArray *)children;
+ (ASLayoutOptions *)layoutOptionsForChild:(id<ASLayoutable>)child;
+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id<ASLayoutable>)child;
+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass;
@end

View File

@ -16,6 +16,8 @@
#import "ASInternalHelpers.h"
#import "ASLayout.h"
#import <objc/runtime.h>
static NSString * const kDefaultChildKey = @"kDefaultChildKey";
static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
@ -25,12 +27,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
@implementation ASLayoutSpec
@synthesize spacingBefore = _spacingBefore;
@synthesize spacingAfter = _spacingAfter;
@synthesize flexGrow = _flexGrow;
@synthesize flexShrink = _flexShrink;
@synthesize flexBasis = _flexBasis;
@synthesize alignSelf = _alignSelf;
@synthesize layoutChildren = _layoutChildren;
- (instancetype)init
@ -39,7 +35,6 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
return nil;
}
_layoutChildren = [NSMutableDictionary dictionary];
_flexBasis = ASRelativeDimensionUnconstrained;
_isMutable = YES;
return self;
}
@ -61,15 +56,34 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
[self setChild:child forIdentifier:kDefaultChildKey];
}
- (id<ASLayoutable>)child
{
return self.layoutChildren[kDefaultChildKey];
}
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
{
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
self.layoutChildren[identifier] = [child finalLayoutable];
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child];
layoutOptions.isMutable = NO;
self.layoutChildren[identifier] = child;
}
- (void)setChildren:(NSArray *)children
{
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count];
for (id<ASLayoutable> child in children) {
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child];
id<ASLayoutable> finalLayoutable = [child finalLayoutable];
layoutOptions.isMutable = NO;
if (finalLayoutable != child) {
ASLayoutOptions *finalLayoutOptions = [layoutOptions copy];
finalLayoutOptions.isMutable = NO;
[ASLayoutSpec associateLayoutOptions:finalLayoutOptions withChild:finalLayoutable];
}
[finalChildren addObject:finalLayoutable];
}
self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren];
}
- (id<ASLayoutable>)childForIdentifier:(NSString *)identifier
@ -77,14 +91,9 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
return self.layoutChildren[identifier];
}
- (void)setChildren:(NSArray *)children
- (id<ASLayoutable>)child
{
ASDisplayNodeAssert(self.isMutable, @"Cannot set properties when layout spec is not mutable");
NSMutableArray *finalChildren = [NSMutableArray arrayWithCapacity:children.count];
for (id<ASLayoutable> child in children) {
[finalChildren addObject:[child finalLayoutable]];
}
self.layoutChildren[kDefaultChildrenKey] = [NSArray arrayWithArray:finalChildren];
return self.layoutChildren[kDefaultChildKey];
}
- (NSArray *)children
@ -92,4 +101,35 @@ static NSString * const kDefaultChildrenKey = @"kDefaultChildrenKey";
return self.layoutChildren[kDefaultChildrenKey];
}
static Class gLayoutOptionsClass = [ASLayoutOptions class];
+ (void)setLayoutOptionsClass:(Class)layoutOptionsClass
{
gLayoutOptionsClass = layoutOptionsClass;
}
+ (ASLayoutOptions *)optionsForChild:(id<ASLayoutable>)child
{
ASLayoutOptions *layoutOptions = [[gLayoutOptionsClass alloc] init];;
[layoutOptions setValuesFromLayoutable:child];
layoutOptions.isMutable = NO;
return layoutOptions;
}
+ (void)associateLayoutOptions:(ASLayoutOptions *)layoutOptions withChild:(id<ASLayoutable>)child
{
objc_setAssociatedObject(child, @selector(setChild:), layoutOptions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (ASLayoutOptions *)layoutOptionsForChild:(id<ASLayoutable>)child
{
ASLayoutOptions *layoutOptions = objc_getAssociatedObject(child, @selector(setChild:));
if (layoutOptions == nil) {
layoutOptions = [self optionsForChild:child];
[self associateLayoutOptions:layoutOptions withChild:child];
}
return objc_getAssociatedObject(child, @selector(setChild:));
}
@end

View File

@ -10,6 +10,7 @@
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/ASLayoutOptions.h>
@class ASLayout;
@class ASLayoutSpec;

View File

@ -72,17 +72,6 @@
_spacing = spacing;
}
- (void)setChildren:(NSArray *)children
{
[super setChildren:children];
#if DEBUG
for (id<ASStackLayoutable> child in children) {
ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStackLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStackLayoutable)]), @"child must conform to ASBaselineLayoutable");
}
#endif
}
- (void)setChild:(id<ASLayoutable>)child forIdentifier:(NSString *)identifier
{
ASDisplayNodeAssert(NO, @"ASStackLayoutSpec only supports setChildren");
@ -91,9 +80,8 @@
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems};
std::vector<id<ASStackLayoutable>> stackChildren = std::vector<id<ASStackLayoutable>>();
for (id<ASStackLayoutable> child in self.children) {
NSAssert([child conformsToProtocol:@protocol(ASStackLayoutable)], @"Child must implement ASStackLayoutable");
std::vector<id<ASLayoutable>> stackChildren = std::vector<id<ASLayoutable>>();
for (id<ASLayoutable> child in self.children) {
stackChildren.push_back(child);
}

View File

@ -8,9 +8,10 @@
*
*/
#import <AsyncDisplayKit/ASLayoutable.h>
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
@protocol ASStackLayoutable <ASLayoutable>
@protocol ASStackLayoutable <NSObject>
/**
* @abstract Additional space to place before this object in the stacking direction.

View File

@ -31,17 +31,6 @@
return self;
}
- (void)setChildren:(NSArray *)children
{
[super setChildren:children];
#if DEBUG
for (id<ASStaticLayoutable> child in children) {
ASDisplayNodeAssert(([child finalLayoutable] == child && [child conformsToProtocol:@protocol(ASStaticLayoutable)]) || ([child finalLayoutable] != child && [[child finalLayoutable] conformsToProtocol:@protocol(ASStaticLayoutable)]), @"child must conform to ASStaticLayoutable");
}
#endif
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
CGSize size = {
@ -50,16 +39,17 @@
};
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:self.children.count];
for (id<ASStaticLayoutable> child in self.children) {
for (id<ASLayoutable> child in self.children) {
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child];
CGSize autoMaxSize = {
constrainedSize.max.width - child.position.x,
constrainedSize.max.height - child.position.y
constrainedSize.max.width - layoutOptions.position.x,
constrainedSize.max.height - layoutOptions.position.y
};
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, child.sizeRange)
ASSizeRange childConstraint = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, layoutOptions.sizeRange)
? ASSizeRangeMake({0, 0}, autoMaxSize)
: ASRelativeSizeRangeResolve(child.sizeRange, size);
: ASRelativeSizeRangeResolve(layoutOptions.sizeRange, size);
ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
sublayout.position = child.position;
sublayout.position = layoutOptions.position;
[sublayouts addObject:sublayout];
}

View File

@ -8,10 +8,9 @@
*
*/
#import <AsyncDisplayKit/ASLayoutable.h>
#import <AsyncDisplayKit/ASRelativeSize.h>
@protocol ASStaticLayoutable<ASLayoutable>
@protocol ASStaticLayoutable
/**
If specified, the child's size is restricted according to this size. Percentages are resolved relative to the static layout spec.

View File

@ -15,15 +15,14 @@
static CGFloat baselineForItem(const ASBaselineLayoutSpecStyle &style,
const ASLayout *layout) {
__weak id<ASBaselineLayoutable> child = (id<ASBaselineLayoutable>) layout.layoutableObject;
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:layout.layoutableObject];
switch (style.baselineAlignment) {
case ASBaselineLayoutBaselineAlignmentNone:
return 0;
case ASBaselineLayoutBaselineAlignmentFirst:
return child.ascender;
return layoutOptions.ascender;
case ASBaselineLayoutBaselineAlignmentLast:
return layout.size.height + child.descender;
return layout.size.height + layoutOptions.descender;
}
}
@ -34,10 +33,10 @@ static CGFloat baselineOffset(const ASBaselineLayoutSpecStyle &style,
const CGFloat maxBaseline)
{
if (style.stackLayoutStyle.direction == ASStackLayoutDirectionHorizontal) {
__weak id<ASBaselineLayoutable> child = (id<ASBaselineLayoutable>)l.layoutableObject;
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject];
switch (style.baselineAlignment) {
case ASBaselineLayoutBaselineAlignmentFirst:
return maxAscender - child.ascender;
return maxAscender - layoutOptions.ascender;
case ASBaselineLayoutBaselineAlignmentLast:
return maxBaseline - baselineForItem(style, l);
case ASBaselineLayoutBaselineAlignmentNone:
@ -91,9 +90,11 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec.
*/
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){
return ((id<ASBaselineLayoutable>)a.layoutableObject).ascender < ((id<ASBaselineLayoutable>)b.layoutableObject).ascender;
ASLayoutOptions *layoutOptionsA = [ASLayoutSpec layoutOptionsForChild:a.layoutableObject];
ASLayoutOptions *layoutOptionsB = [ASLayoutSpec layoutOptionsForChild:b.layoutableObject];
return layoutOptionsA.ascender < layoutOptionsB.ascender;
});
const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : ((id<ASBaselineLayoutable>)(*ascenderIt).layoutableObject).ascender;
const CGFloat maxAscender = baselineIt == positionedLayout.sublayouts.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*ascenderIt).layoutableObject].ascender;
/*
Step 3: Take each child and update its layout position based on the baseline offset.
@ -106,8 +107,8 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
CGPoint p = CGPointZero;
BOOL first = YES;
auto stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
__weak id<ASBaselineLayoutable> child = (id<ASBaselineLayoutable>) l.layoutableObject;
p = p + directionPoint(stackStyle.direction, child.spacingBefore, 0);
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.layoutableObject];
p = p + directionPoint(stackStyle.direction, layoutOptions.spacingBefore, 0);
if (first) {
// if this is the first item use the previously computed start point
p = l.position;
@ -124,9 +125,9 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
// node from baselines and not bounding boxes.
CGFloat spacingAfterBaseline = 0;
if (stackStyle.direction == ASStackLayoutDirectionVertical) {
spacingAfterBaseline = child.descender;
spacingAfterBaseline = layoutOptions.descender;
}
p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + child.spacingAfter + spacingAfterBaseline, 0);
p = p + directionPoint(stackStyle.direction, stackDimension(stackStyle.direction, l.size) + layoutOptions.spacingAfter + spacingAfterBaseline, 0);
return l;
});
@ -151,12 +152,12 @@ ASBaselinePositionedLayout ASBaselinePositionedLayout::compute(const ASStackPosi
/*
Step 5: finally, we must find the smallest descender (descender is negative). This is since ASBaselineLayoutSpec implements
ASBaselineLayoutable and needs an ascender and descender to lay itself out properly.
ASLayoutable and needs an ascender and descender to lay itself out properly.
*/
const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){
return a.position.y + a.size.height < b.position.y + b.size.height;
});
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : ((id<ASBaselineLayoutable>)(*descenderIt).layoutableObject).descender;
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : [ASLayoutSpec layoutOptionsForChild:(*descenderIt).layoutableObject].descender;
return {stackedChildren, crossSize, maxAscender, minDescender};
}

View File

@ -19,7 +19,8 @@ static CGFloat crossOffset(const ASStackLayoutSpecStyle &style,
const ASStackUnpositionedItem &l,
const CGFloat crossSize)
{
switch (alignment(l.child.alignSelf, style.alignItems)) {
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child];
switch (alignment(layoutOptions.alignSelf, style.alignItems)) {
case ASStackLayoutAlignItemsEnd:
return crossSize - crossDimension(style.direction, l.layout.size);
case ASStackLayoutAlignItemsCenter:
@ -48,14 +49,15 @@ static ASStackPositionedLayout stackedLayout(const ASStackLayoutSpecStyle &style
CGPoint p = directionPoint(style.direction, offset, 0);
BOOL first = YES;
auto stackedChildren = AS::map(unpositionedLayout.items, [&](const ASStackUnpositionedItem &l) -> ASLayout *{
p = p + directionPoint(style.direction, l.child.spacingBefore, 0);
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child];
p = p + directionPoint(style.direction, layoutOptions.spacingBefore, 0);
if (!first) {
p = p + directionPoint(style.direction, style.spacing, 0);
}
first = NO;
l.layout.position = p + directionPoint(style.direction, 0, crossOffset(style, l, crossSize));
p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + l.child.spacingAfter, 0);
p = p + directionPoint(style.direction, stackDimension(style.direction, l.layout.size) + layoutOptions.spacingAfter, 0);
return l.layout;
});
return {stackedChildren, crossSize};

View File

@ -16,7 +16,7 @@
struct ASStackUnpositionedItem {
/** The original source child. */
id<ASStackLayoutable> child;
id<ASLayoutable> child;
/** The proposed layout. */
ASLayout *layout;
};
@ -31,7 +31,7 @@ struct ASStackUnpositionedLayout {
const CGFloat violation;
/** Given a set of children, computes the unpositioned layouts for those children. */
static ASStackUnpositionedLayout compute(const std::vector<id<ASStackLayoutable>> &children,
static ASStackUnpositionedLayout compute(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange);
};

View File

@ -18,14 +18,15 @@
/**
Sizes the child given the parameters specified, and returns the computed layout.
*/
static ASLayout *crossChildLayout(const id<ASStackLayoutable> child,
static ASLayout *crossChildLayout(const id<ASLayoutable> child,
const ASStackLayoutSpecStyle style,
const CGFloat stackMin,
const CGFloat stackMax,
const CGFloat crossMin,
const CGFloat crossMax)
{
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child];
const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems);
// stretched children will have a cross dimension of at least crossMin
const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0;
const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax);
@ -75,7 +76,8 @@ static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedIt
const CGFloat childCrossMax = it == layouts.end() ? 0 : crossDimension(style.direction, it->layout.size);
for (auto &l : layouts) {
const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems);
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child];
const ASStackLayoutAlignItems alignItems = alignment(layoutOptions.alignSelf, style.alignItems);
const CGFloat cross = crossDimension(style.direction, l.layout.size);
const CGFloat stack = stackDimension(style.direction, l.layout.size);
@ -111,7 +113,8 @@ static CGFloat computeStackDimensionSum(const std::vector<ASStackUnpositionedIte
// Start from default spacing between each child:
children.empty() ? 0 : style.spacing * (children.size() - 1),
[&](CGFloat x, const ASStackUnpositionedItem &l) {
return x + l.child.spacingBefore + l.child.spacingAfter;
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:l.child];
return x + layoutOptions.spacingBefore + layoutOptions.spacingAfter;
});
// Sum up the childrens' dimensions (including spacing) in the stack direction.
@ -180,22 +183,23 @@ static std::function<BOOL(const ASStackUnpositionedItem &)> isFlexibleInViolatio
if (fabs(violation) < kViolationEpsilon) {
return [](const ASStackUnpositionedItem &l) { return NO; };
} else if (violation > 0) {
return [](const ASStackUnpositionedItem &l) { return l.child.flexGrow; };
return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexGrow; };
} else {
return [](const ASStackUnpositionedItem &l) { return l.child.flexShrink; };
return [](const ASStackUnpositionedItem &l) { return [ASLayoutSpec layoutOptionsForChild:l.child].flexShrink; };
}
}
ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASStackLayoutable> child)
ASDISPLAYNODE_INLINE BOOL isFlexibleInBothDirections(id<ASLayoutable> child)
{
return child.flexGrow && child.flexShrink;
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child];
return layoutOptions.flexGrow && layoutOptions.flexShrink;
}
/**
If we have a single flexible (both shrinkable and growable) child, and our allowed size range is set to a specific
number then we may avoid the first "intrinsic" size calculation.
*/
ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASStackLayoutable>> &children,
ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{
@ -283,7 +287,7 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
Performs the first unconstrained layout of the children, generating the unpositioned items that are then flexed and
stretched.
*/
static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<id<ASStackLayoutable>> &children,
static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStackDimension(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange,
const CGSize size,
@ -292,9 +296,10 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
return AS::map(children, [&](id<ASStackLayoutable> child) -> ASStackUnpositionedItem {
const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, child.flexBasis);
const CGFloat exactStackDimension = ASRelativeDimensionResolve(child.flexBasis, stackDimension(style.direction, size));
return AS::map(children, [&](id<ASLayoutable> child) -> ASStackUnpositionedItem {
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:child];
const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, layoutOptions.flexBasis);
const CGFloat exactStackDimension = ASRelativeDimensionResolve(layoutOptions.flexBasis, stackDimension(style.direction, size));
if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
return { child, [ASLayout layoutWithLayoutableObject:child size:{0, 0}] };
@ -312,7 +317,7 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
});
}
ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<id<ASStackLayoutable>> &children,
ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<id<ASLayoutable>> &children,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{

View File

@ -94,7 +94,7 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
foregroundNode.staticSize = {10, 10};
foregroundNode.flexGrow = YES;
[ASLayoutSpec layoutOptionsForChild:foregroundNode].flexGrow = YES;
ASCenterLayoutSpec *layoutSpec =
[ASCenterLayoutSpec

View File

@ -41,8 +41,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
];
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.staticSize = subnodeSize;
subnode.flexGrow = flex;
subnode.flexShrink = flex;
[ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = flex;
[ASLayoutSpec layoutOptionsForChild:subnode].flexShrink = flex;
}
return subnodes;
}
@ -114,7 +114,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
((ASDisplayNode *)subnodes[1]).flexShrink = YES;
[ASLayoutSpec layoutOptionsForChild:((ASDisplayNode *)subnodes[1])].flexShrink = YES;
// Width is 75px--that's less than the sum of the widths of the children, which is 100px.
static ASSizeRange kSize = {{75, 0}, {75, 150}};
@ -204,23 +204,25 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20;
ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:subnodes[1]];
ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:subnodes[2]];
layoutOptions1.spacingBefore = 10;
layoutOptions2.spacingBefore = 20;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"];
// Reset above spacing values
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0;
layoutOptions1.spacingBefore = 0;
layoutOptions2.spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20;
layoutOptions1.spacingAfter = 10;
layoutOptions2.spacingAfter = 20;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"];
// Reset above spacing values
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0;
layoutOptions1.spacingAfter = 0;
layoutOptions2.spacingAfter = 0;
style.spacing = 10;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10;
layoutOptions1.spacingBefore = -10;
layoutOptions2.spacingAfter = -10;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"];
}
@ -236,9 +238,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30;
// width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@ -254,9 +256,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
subnode2.staticSize = {50, 50};
ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1];
child1.flexBasis = ASRelativeDimensionMakeWithPercent(1);
child1.flexGrow = YES;
child1.flexShrink = YES;
ASLayoutOptions *layoutOptions1 = [ASLayoutSpec layoutOptionsForChild:child1];
layoutOptions1.flexBasis = ASRelativeDimensionMakeWithPercent(1);
layoutOptions1.flexGrow = YES;
layoutOptions1.flexShrink = YES;
static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
[self testStackLayoutSpecWithStyle:style children:@[child1, subnode2] sizeRange:kFixedWidth subnodes:@[subnode1, subnode2] identifier:nil];
@ -271,11 +274,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
subnode1.staticSize = {100, 100};
subnode1.flexShrink = YES;
[ASLayoutSpec layoutOptionsForChild:subnode1].flexShrink = YES;
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
subnode2.staticSize = {50, 50};
subnode2.flexShrink = YES;
[ASLayoutSpec layoutOptionsForChild:subnode2].flexShrink = YES;
NSArray *subnodes = @[subnode1, subnode2];
static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}};
@ -291,7 +294,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
subnode2.staticSize = {50, 50};
subnode2.alignSelf = ASStackLayoutAlignSelfCenter;
[ASLayoutSpec layoutOptionsForChild:subnode2].alignSelf = ASStackLayoutAlignSelfCenter;
NSArray *subnodes = @[subnode1, subnode2];
static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
@ -311,9 +314,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@ -332,9 +335,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@ -353,9 +356,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@ -374,9 +377,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30;
static ASSizeRange kVariableSize = {{200, 200}, {300, 300}};
// all children should be 200px wide
@ -396,9 +399,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].spacingBefore = 0;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].spacingBefore = 20;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].spacingBefore = 30;
static ASSizeRange kVariableSize = {{50, 50}, {300, 300}};
// all children should be 150px wide
@ -419,8 +422,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.flexGrow = YES;
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10);
ASLayoutOptions *layoutOptions = [ASLayoutSpec layoutOptionsForChild:subnode];
layoutOptions.flexGrow = YES;
layoutOptions.flexBasis = ASRelativeDimensionMakeWithPoints(10);
}
// width 300px; height 0-150px.
@ -439,12 +443,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.flexGrow = YES;
[ASLayoutSpec layoutOptionsForChild:subnode].flexGrow = YES;
}
// This should override the intrinsic size of 50pts and instead compute to 50% = 100pts.
// The result should be that the red box is twice as wide as the blue and gree boxes after flexing.
((ASStaticSizeDisplayNode *)subnodes[0]).flexBasis = ASRelativeDimensionMakeWithPercent(0.5);
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexBasis = ASRelativeDimensionMakeWithPercent(0.5);
static ASSizeRange kSize = {{200, 0}, {200, INFINITY}};
[self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil];
@ -460,7 +464,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50};
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20);
[ASLayoutSpec layoutOptionsForChild:subnode].flexBasis = ASRelativeDimensionMakeWithPoints(20);
}
static ASSizeRange kSize = {{300, 0}, {300, 150}};
@ -478,8 +482,9 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000};
ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]];
child2.flexGrow = YES;
child2.flexShrink = YES;
ASLayoutOptions *layoutOptions2 = [ASLayoutSpec layoutOptionsForChild:child2];
layoutOptions2.flexGrow = YES;
layoutOptions2.flexShrink = YES;
// If cross axis stretching occurred *before* flexing, then the blue child would be stretched to 3000 points tall.
// Instead it should be stretched to 300 points tall, matching the red child and not overlapping the green inset.
@ -504,13 +509,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50};
((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[0])].flexShrink = YES;
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[1])].flexShrink = NO;
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50};
((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES;
[ASLayoutSpec layoutOptionsForChild:((ASStaticSizeDisplayNode *)subnodes[2])].flexShrink = YES;
// A width of 400px results in a violation of 200px. This is distributed equally among each flexible child,
// causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px.

View File

@ -10,4 +10,4 @@ SPEC CHECKSUMS:
FBSnapshotTestCase: 3dc3899168747a0319c5278f5b3445c13a6532dd
OCMock: a6a7dc0e3997fb9f35d99f72528698ebf60d64f2
COCOAPODS: 0.37.2
COCOAPODS: 0.35.0

View File

@ -58,7 +58,9 @@
1A943BF0259746F18D6E423F /* Frameworks */,
1AE410B73DA5C3BD087ACDD7 /* Pods */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
05E2128219D4DB510098F589 /* Products */ = {
isa = PBXGroup;

View File

@ -135,7 +135,9 @@ static const CGFloat kInnerPadding = 10.0f;
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize);
_textNode.flexShrink = YES;
ASLayoutOptions *textNodeOptions = [[ASLayoutOptions alloc] init];
textNodeOptions.flexShrink = YES;
[ASLayoutSpec associateLayoutOptions:textNodeOptions withChild:_textNode];
ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init];
stackSpec.direction = ASStackLayoutDirectionHorizontal;