[Layout] Layout API based on content area (#2110)

* Initial commit for adding a size constraint to ASLayoutable's

* Some more commits

* Fix sample projects in extra/

* Remove preferredFrameSize test of ASEditableTextNode

* Remove preferredFrameSize from examples_extra

* Add deprecation warning to -[ASDisplayNode preferredFrameSize]

* Add deprecation warning to -[ASDisplayNode measureWithSizeRange:]

* Commit

* Commit

* Remove ASRelativeSizeRange

* Make ASRelativeSize private

* Adjust examples

* Improve setting of -[ASLayoutable size] with points or fractions

* Add ASWrapperLayoutSpec

* Improve creation of ASRelativeDimension

* Add `preferredFrameSize` back and add deprecated logging

* Add `layoutSpecBlock` setter and getter and add locking for it

* Add better documentation and fix macros to create ASRelativeDimension

* Create new ASSizeRangeMake with just a CGSize as parameter

* Update Kitten and Social App Layout example

* Add layoutThatFits: and deprecate measure:

* Rename ASRelativeDimension to ASDimension

* Fix examples for ASDimension renaming

* Remove fancy height and width setter

* Fix ASDimension helper

* Rename -[ASLayout layoutableObject] to -[ASLayout layoutable]

* Update layout related methods and more clearer documentation around how to use it

* Deprecate old ASLayout class constructors

* Don't unnecessary recalculate layout if constrained or parent size did not change

* Use shared pointer for ASDisplayNodeLayout

* Fix rebase conflicts

* Add documentation and move implementation in mm file of ASDisplayNodeLayout

* Fix test errors

* Rename ASSize to ASLayoutableSize

* Address comments

* Rename setSizeFromCGSize to setSizeWithCGSize

* Improve inline functions in ASDimension

* Fix rebase conflicts
This commit is contained in:
Michael Schneider
2016-09-07 17:44:48 +02:00
committed by Adlai Holler
parent 2bfeb6de92
commit 8897614f0e
109 changed files with 2089 additions and 1304 deletions

View File

@@ -203,6 +203,9 @@
69708BA61D76386D005C3CF9 /* ASEqualityHashHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */; };
69708BA71D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; };
69708BA81D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; };
6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; };
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; };
695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */; };
697B315A1CFE4B410049936F /* ASEditableTextNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */; };
697C0DE41CF38F28001DE0D4 /* ASLayoutValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = 697C0DE11CF38F28001DE0D4 /* ASLayoutValidation.h */; };
697C0DE51CF38F28001DE0D4 /* ASLayoutValidation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 697C0DE21CF38F28001DE0D4 /* ASLayoutValidation.mm */; };
@@ -969,6 +972,8 @@
696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBackgroundLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEqualityHashHelpers.h; path = TextKit/ASEqualityHashHelpers.h; sourceTree = "<group>"; };
69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEqualityHashHelpers.mm; path = TextKit/ASEqualityHashHelpers.mm; sourceTree = "<group>"; };
6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = "<group>"; };
6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = "<group>"; };
697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASEditableTextNodeTests.m; sourceTree = "<group>"; };
697C0DE11CF38F28001DE0D4 /* ASLayoutValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutValidation.h; path = AsyncDisplayKit/Layout/ASLayoutValidation.h; sourceTree = "<group>"; };
697C0DE21CF38F28001DE0D4 /* ASLayoutValidation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutValidation.mm; path = AsyncDisplayKit/Layout/ASLayoutValidation.mm; sourceTree = "<group>"; };
@@ -1518,6 +1523,8 @@
044285051BAA63FE00D16268 /* ASBatchFetching.h */,
044285061BAA63FE00D16268 /* ASBatchFetching.m */,
251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */,
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */,
AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */,
058D0A08195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm */,
@@ -1526,6 +1533,8 @@
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */,
058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */,
058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */,
6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */,
6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */,
69E100691CA89CB600D88C1B /* ASEnvironmentInternal.h */,
69E1006A1CA89CB600D88C1B /* ASEnvironmentInternal.mm */,
68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */,
@@ -1551,8 +1560,6 @@
ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */,
83A7D9581D44542100BF333E /* ASWeakMap.h */,
83A7D9591D44542100BF333E /* ASWeakMap.m */,
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
);
path = Private;
sourceTree = "<group>";
@@ -1808,6 +1815,7 @@
697C0DE41CF38F28001DE0D4 /* ASLayoutValidation.h in Headers */,
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */,
34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */,
695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */,
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
@@ -2187,6 +2195,7 @@
68355B311CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */,
9CFFC6C01CCAC73C006A6476 /* ASViewController.mm in Sources */,
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
257754AC1BEE44CD00737CA5 /* ASTextKitRenderer.mm in Sources */,
8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */,
@@ -2368,6 +2377,7 @@
DB78412E1C6BCE1600A9E2B4 /* _ASTransitionContext.m in Sources */,
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */,
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */,
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */,
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,

View File

@@ -491,10 +491,17 @@
spec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentEdgeInsets child:spec];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) {
stack.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(self.preferredFrameSize);
#if DEBUG
NSLog(@"Using -[ASDisplayNde preferredFrameSize] is deprecated.");
#endif
stack.width = ASDimensionMake(ASDimensionUnitPoints, self.preferredFrameSize.width);
stack.height = ASDimensionMake(ASDimensionUnitPoints, self.preferredFrameSize.height);
spec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack]];
}
#pragma clang diagnostic pop
if (_backgroundImageNode.image) {
spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec background:_backgroundImageNode];

View File

@@ -14,6 +14,38 @@
@interface ASDisplayNode (Deprecated)
/**
* @abstract Asks the node to measure and return the size that best fits its subnodes.
*
* @param constrainedSize The maximum size the receiver should fit in.
*
* @return A new size that fits the receiver's subviews.
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
* -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see measureWithSizeRange:
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*
* @deprecated Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout
*/
- (CGSize)measure:(CGSize)constrainedSize ASDISPLAYNODE_DEPRECATED;
/**
* @abstract Calculate a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver and its children.
*
* @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout or ASCalculateLayout instead
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED;
/**
* @abstract Called whenever the visiblity of the node changed.
*

View File

@@ -118,6 +118,19 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
/**
* ASDisplayNode's implementation of -layoutThatFits:parentSize: calls this method to resolve the node's size
* against parentSize, intersect it with constrainedSize, and call -calculateLayoutThatFits: with the result.
*
* In certain advanced cases, you may want to customize this logic. Overriding this method allows you to receive all
* three parameters and do the computation yourself.
*
* @warning Overriding this method should be done VERY rarely.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize;
/**
* @abstract Return the calculated size.
*
@@ -130,7 +143,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* @note Subclasses that override are committed to manual layout. Therefore, -layout: must be overriden to layout all subnodes or subviews.
*
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
* @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or layoutThatFits:parentSize: instead.
*/
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize;

View File

@@ -248,26 +248,7 @@ NS_ASSUME_NONNULL_BEGIN
/** @name Managing dimensions */
/**
* @abstract Asks the node to measure and return the size that best fits its subnodes.
*
* @param constrainedSize The maximum size the receiver should fit in.
*
* @return A new size that fits the receiver's subviews.
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
* -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see measureWithSizeRange:
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (CGSize)measure:(CGSize)constrainedSize;
/**
* @abstract Asks the node to measure a layout based on given size range.
* @abstract Asks the node to return a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
@@ -281,8 +262,7 @@ NS_ASSUME_NONNULL_BEGIN
*
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;
/**
* @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
@@ -317,16 +297,6 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout;
/**
* @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out
* a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content
* size. For example, this property could be set on an ASImageNode to display at a size different from the underlying
* image size.
*
* @return The preferred frame size of this node
*/
@property (nonatomic, assign, readwrite) CGSize preferredFrameSize;
/** @name Managing the nodes hierarchy */
@@ -625,7 +595,6 @@ NS_ASSUME_NONNULL_BEGIN
/**
* Convenience methods for debugging.
*/
@interface ASDisplayNode (Debugging) <ASLayoutableAsciiArtProtocol>
/**
@@ -838,6 +807,20 @@ NS_ASSUME_NONNULL_BEGIN
- (void)cancelLayoutTransition;
#pragma mark - Deprecated
/**
* @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out
* a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content
* size. For example, this property could be set on an ASImageNode to display at a size different from the underlying
* image size.
*
* @return The preferred frame size of this node
*
* @deprecated Deprecated in version 2.0: Use sizing properties instead: height, minHeight, maxHeight, width, minWidth, maxWidth
*/
@property (nonatomic, assign, readwrite) CGSize preferredFrameSize ASDISPLAYNODE_DEPRECATED;
@end
/*

View File

@@ -26,6 +26,7 @@
#import "ASEqualityHelpers.h"
#import "ASRunLoopQueue.h"
#import "ASEnvironmentInternal.h"
#import "ASDimension.h"
#import "ASInternalHelpers.h"
#import "ASLayout.h"
@@ -68,14 +69,19 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
@implementation ASDisplayNode
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis,
alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType;
// Dynamic properties for ASLayoutables
@dynamic layoutableType, size;
// Dynamic properties for sizing
@dynamic width, height, minWidth, maxWidth, minHeight, maxHeight;
// Dynamic properties for stack spec
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender;
// Dynamic properties for static spec
@dynamic layoutPosition;
@synthesize name = _name;
@synthesize preferredFrameSize = _preferredFrameSize;
@synthesize isFinalLayoutable = _isFinalLayoutable;
@synthesize threadSafeBounds = _threadSafeBounds;
@synthesize layoutSpecBlock = _layoutSpecBlock;
static BOOL suppressesInvalidCollectionUpdateExceptions = NO;
@@ -192,12 +198,16 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (self != [ASDisplayNode class]) {
// Subclasses should never override these
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self));
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self));
NSString *classString = NSStringFromClass(self);
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(measure:)), @"Subclass %@ must not override measure: method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead overwrite calculateLayoutThatFits:", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", classString);
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", classString);
}
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
@@ -294,10 +304,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self _staticInitialize];
_contentsScaleForDisplay = ASScreenScale();
_displaySentinel = [[ASSentinel alloc] init];
_preferredFrameSize = CGSizeZero;
_size = ASLayoutableSizeMake();
_preferredFrameSize = CGSizeZero;
_environmentState = ASEnvironmentStateMakeDefault();
_calculatedDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>();
_defaultLayoutTransitionDuration = 0.2;
_defaultLayoutTransitionDelay = 0.0;
_defaultLayoutTransitionOptions = UIViewAnimationOptionBeginFromCurrentState;
@@ -449,11 +462,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return !(_hierarchyState & ASHierarchyStateRasterized);
}
- (BOOL)__shouldSize
{
return YES;
}
- (UIView *)_viewToLoad
{
UIView *view;
@@ -639,46 +647,72 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return _flags.layerBacked;
}
#pragma mark - Layout measurement calculation
#pragma mark - Layout measurement and sizing
- (CGSize)measure:(CGSize)constrainedSize
{
return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayoutableSize)size
{
ASDN::MutexLocker l(__instanceLock__);
if (! [self shouldMeasureWithSizeRange:constrainedSize]) {
ASDisplayNodeAssertNotNil(_calculatedLayout, @"-[ASDisplayNode measureWithSizeRange:] _layout should not be nil! %@", self);
return _calculatedLayout ? : [ASLayout layoutWithLayoutableObject:self constrainedSizeRange:constrainedSize size:CGSizeZero];
return _size;
}
- (void)setSize:(ASLayoutableSize)size
{
ASDN::MutexLocker l(__instanceLock__);
if (ASLayoutableSizeEqualToLayoutableSize(_size, size) == NO) {
_size = size;
[self invalidateCalculatedLayout];
}
}
ASLayoutableSizeForwarding
ASLayoutableSizeHelperForwarding
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
return [self measureWithSizeRange:constrainedSize];
#pragma clang diagnostic pop
}
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
{
ASDN::MutexLocker l(__instanceLock__);
if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:parentSize] == NO) {
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _layout should not be nil! %@", self);
return _calculatedDisplayNodeLayout->layout ? : [ASLayout layoutWithLayoutable:self size:{0, 0}];
}
[self cancelLayoutTransition];
ASLayout *previousLayout = _calculatedLayout;
ASLayout *newLayout = [self calculateLayoutThatFits:constrainedSize];
// Prepare for layout transition
auto previousLayout = _calculatedDisplayNodeLayout;
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
[self calculateLayoutThatFits:constrainedSize restrictedToSize:_size relativeToParentSize:parentSize],
constrainedSize,
parentSize
);
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
pendingLayout:newLayout
pendingLayout:pendingLayout
previousLayout:previousLayout];
// Only complete the pending layout transition if the node is not a subnode of a node that is currently
// in a layout transition
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) {
// Complete the pending layout transition immediately
[self _completePendingLayoutTransition];
}
ASDisplayNodeAssertNotNil(newLayout, @"-[ASDisplayNode measureWithSizeRange:] newLayout should not be nil! %@", self);
return newLayout;
ASDisplayNodeAssertNotNil(pendingLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newLayout should not be nil! %@", self);
return pendingLayout->layout;
}
- (BOOL)shouldMeasureWithSizeRange:(ASSizeRange)constrainedSize
- (BOOL)shouldCalculateLayoutWithConstrainedSize:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
{
ASDN::MutexLocker l(__instanceLock__);
if (![self __shouldSize]) {
return NO;
}
// Don't remeasure if in layout pending state and a new transition already started
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
ASLayoutableContext context = ASLayoutableGetCurrentContext();
if (ASLayoutableContextIsNull(context) || _pendingTransitionID != context.transitionID) {
@@ -686,15 +720,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
}
}
// Only generate a new layout if:
// - The current layout is dirty
// - The passed constrained size is different than the layout's constrained size
return ([self _hasDirtyLayout] || !ASSizeRangeEqualToSizeRange(constrainedSize, _calculatedLayout.constrainedSizeRange));
}
- (BOOL)_hasDirtyLayout
{
return _calculatedLayout == nil || _calculatedLayout.isDirty;
// Check if display node layout is still valid
return _calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize) == NO;
}
- (ASLayoutableType)layoutableType
@@ -727,14 +754,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
shouldMeasureAsync:(BOOL)shouldMeasureAsync
measurementCompletion:(void(^)())completion
{
if (_calculatedLayout == nil) {
// constrainedSizeRange returns a struct and is invalid to call on nil.
// Defaulting to CGSizeZero can cause negative values in client layout code.
if (_calculatedDisplayNodeLayout->layout == nil) {
// No measure pass happened before, it's not possible to reuse the constrained size for the transition
// Using CGSizeZero for the sizeRange can cause negative values in client layout code.
return;
}
[self invalidateCalculatedLayout];
[self transitionLayoutWithSizeRange:_calculatedLayout.constrainedSizeRange
[self transitionLayoutWithSizeRange:_calculatedDisplayNodeLayout->constrainedSize
animated:animated
shouldMeasureAsync:shouldMeasureAsync
measurementCompletion:completion];
@@ -747,7 +774,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
measurementCompletion:(void(^)())completion
{
// Passed constrainedSize is the the same as the node's current constrained size it's a noop
if ([self shouldMeasureWithSizeRange:constrainedSize] == NO) {
ASDisplayNodeAssertMainThread();
if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:constrainedSize.max] == NO) {
return;
}
@@ -758,6 +786,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
int32_t transitionID = [self _startNewTransition];
// Move all subnodes in a pending state
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");
node.hierarchyState |= ASHierarchyStateLayoutPending;
@@ -777,7 +806,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASDN::MutexLocker l(__instanceLock__);
BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO);
self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x
newLayout = [self calculateLayoutThatFits:constrainedSize];
newLayout = [self calculateLayoutThatFits:constrainedSize
restrictedToSize:_size
relativeToParentSize:constrainedSize.max];
if (automaticallyManagesSubnodesDisabled) {
self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x
}
@@ -797,10 +828,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if ([self _shouldAbortTransitionWithID:transitionID]) {
return;
}
// Update display node layout
auto previousLayout = _calculatedDisplayNodeLayout;
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
newLayout,
constrainedSize,
constrainedSize.max
);
[self setCalculatedDisplayNodeLayout:pendingLayout];
ASLayout *previousLayout = _calculatedLayout;
[self setCalculatedLayout:newLayout];
// Apply complete layout transitions for all subnodes
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
[node _completePendingLayoutTransition];
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
@@ -808,13 +846,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self _finishOrCancelTransition];
// Measurement pass completion
if (completion) {
completion();
}
// Setup pending layout transition for animation
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
pendingLayout:newLayout
pendingLayout:pendingLayout
previousLayout:previousLayout];
// Setup context for pending layout transition. we need to hold a strong reference to the context
_pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated
@@ -916,8 +955,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
}
/*
* Hook for subclasse to perform an animation based on the given ASContextTransitioning. By default a fade in and out
* animation is provided.
* Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out
* animation is provided.
*/
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
{
@@ -930,7 +969,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
ASDisplayNode *node = self;
NSAssert(node.isNodeLoaded == YES, @"Invalid node state");
NSAssert([context isAnimated] == YES, @"Can't animate a non-animatable context");
NSArray<ASDisplayNode *> *removedSubnodes = [context removedSubnodes];
NSMutableArray<UIView *> *removedViews = [NSMutableArray array];
@@ -1031,7 +1069,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
{
ASDN::MutexLocker l(__instanceLock__);
if (_pendingLayoutTransition) {
[self setCalculatedLayout:_pendingLayoutTransition.pendingLayout];
[self setCalculatedDisplayNodeLayout:_pendingLayoutTransition.pendingLayout];
[self _completeLayoutTransition:_pendingLayoutTransition];
}
[self _pendingLayoutTransitionDidComplete];
@@ -1072,7 +1110,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (_placeholderEnabled && [self _displaysAsynchronously] && self.contents == nil) {
// Zero-sized nodes do not require a placeholder.
CGSize layoutSize = (_calculatedLayout ? _calculatedLayout.size : CGSizeZero);
ASLayout *layout = _calculatedDisplayNodeLayout->layout;
CGSize layoutSize = (layout ? layout.size : CGSizeZero);
if (CGSizeEqualToSize(layoutSize, CGSizeZero)) {
return;
}
@@ -1263,8 +1302,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
__instanceLock__.lock();
if (_calculatedLayout == nil) {
// Can't proceed without a layout as no constrained size would be available
if (_calculatedDisplayNodeLayout->layout == nil) {
// Can't proceed without a layout as no constrained size would be available. If not layout exists at this moment
// no measurement pass did happen just bail out for now
__instanceLock__.unlock();
return;
}
@@ -1281,11 +1321,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
}
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
[self measureWithSizeRange:_calculatedLayout.constrainedSizeRange];
[self layoutThatFits:_calculatedDisplayNodeLayout->constrainedSize];
CGRect oldBounds = self.bounds;
CGSize oldSize = oldBounds.size;
CGSize newSize = _calculatedLayout.size;
CGSize newSize = _calculatedDisplayNodeLayout->layout.size;
if (! CGSizeEqualToSize(oldSize, newSize)) {
self.bounds = (CGRect){ oldBounds.origin, newSize };
@@ -1346,8 +1386,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
CGSize calculatedLayoutSize = CGSizeZero;
{
ASDN::MutexLocker l(__instanceLock__);
hasDirtyLayout = [self _hasDirtyLayout];
calculatedLayoutSize = _calculatedLayout.size;
hasDirtyLayout = _calculatedDisplayNodeLayout->isDirty();
calculatedLayoutSize = _calculatedDisplayNodeLayout->layout.size;
}
// If no measure pass happened or the bounds changed between layout passes we manually trigger a measurement pass
@@ -1356,8 +1396,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
if (CGRectEqualToRect(bounds, CGRectZero)) {
LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
} else {
// This is a no op if the bounds size is the same as the cosntrained size we used to create the layout previously
[self measureWithSizeRange:ASSizeRangeMake(bounds.size, bounds.size)];
[self layoutThatFits:ASSizeRangeMake(bounds.size)];
}
}
}
@@ -2215,6 +2254,14 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
#pragma mark - For Subclasses
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize
{
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutableSizeResolve(_size, parentSize));
return [self calculateLayoutThatFits:resolvedRange];
}
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
__ASDisplayNodeCheckForLayoutMethodOverrides;
@@ -2231,14 +2278,14 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection);
layoutSpec.isMutable = NO;
ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize];
ASLayout *layout = [layoutSpec layoutThatFits:constrainedSize];
ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec);
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.
BOOL isFinalLayoutable = (layout.layoutableObject != self);
BOOL isFinalLayoutable = (layout.layoutable != self);
if (isFinalLayoutable) {
layout.position = CGPointZero;
layout = [ASLayout layoutWithLayoutableObject:self constrainedSizeRange:constrainedSize size:layout.size sublayouts:@[layout]];
layout = [ASLayout layoutWithLayoutable:self size:layout.size sublayouts:@[layout]];
#if LAYOUT_VALIDATION
ASLayoutableValidateLayout(layout);
#endif
@@ -2248,9 +2295,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
// If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used,
// assume that the default implementation of -calculateSizeThatFits: returns it.
CGSize size = [self calculateSizeThatFits:constrainedSize.max];
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:ASSizeRangeClamp(constrainedSize, size)];
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil];
}
}
@@ -2259,7 +2304,13 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
__ASDisplayNodeCheckForLayoutMethodOverrides;
ASDN::MutexLocker l(__instanceLock__);
return _preferredFrameSize;
// Handle deprecated preferred frame size.
if (CGSizeEqualToSize(_preferredFrameSize, CGSizeZero) == NO) {
return _preferredFrameSize;
}
return CGSizeZero;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
@@ -2276,41 +2327,48 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
return [[ASLayoutSpec alloc] init];
}
- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
{
// For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");
ASDN::MutexLocker l(__instanceLock__);
_layoutSpecBlock = [layoutSpecBlock copy];
}
- (ASLayoutSpecBlock)layoutSpecBlock
{
ASDN::MutexLocker l(__instanceLock__);
return _layoutSpecBlock;
}
- (ASLayout *)calculatedLayout
{
ASDN::MutexLocker l(__instanceLock__);
return _calculatedLayout;
return _calculatedDisplayNodeLayout->layout;
}
- (void)setCalculatedLayout:(ASLayout *)calculatedLayout
- (void)setCalculatedDisplayNodeLayout:(std::shared_ptr<ASDisplayNodeLayout>)displayNodeLayout
{
ASDN::MutexLocker l(__instanceLock__);
ASDisplayNodeAssertTrue(calculatedLayout.layoutableObject == self);
ASDisplayNodeAssertTrue(calculatedLayout.size.width >= 0.0);
ASDisplayNodeAssertTrue(calculatedLayout.size.height >= 0.0);
ASDisplayNodeAssertTrue(displayNodeLayout->layout.layoutable == self);
ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.width >= 0.0);
ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.height >= 0.0);
_calculatedLayout = calculatedLayout;
_calculatedDisplayNodeLayout = displayNodeLayout;
}
- (CGSize)calculatedSize
{
ASDN::MutexLocker l(__instanceLock__);
return _calculatedLayout.size;
return _calculatedDisplayNodeLayout->layout.size;
}
- (ASSizeRange)constrainedSizeForCalculatedLayout
{
ASDN::MutexLocker l(__instanceLock__);
return _calculatedLayout.constrainedSizeRange;
}
- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
{
// For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");
_layoutSpecBlock = layoutSpecBlock;
return _calculatedDisplayNodeLayout->constrainedSize;
}
- (void)setPendingTransitionID:(int32_t)pendingTransitionID
@@ -2331,7 +2389,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
ASDN::MutexLocker l(__instanceLock__);
if (! CGSizeEqualToSize(_preferredFrameSize, preferredFrameSize)) {
_preferredFrameSize = preferredFrameSize;
self.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(_preferredFrameSize);
self.width = ASDimensionMake(preferredFrameSize.width);
self.height = ASDimensionMake(preferredFrameSize.height);
[self invalidateCalculatedLayout];
}
}
@@ -2363,9 +2424,9 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
{
ASDN::MutexLocker l(__instanceLock__);
// This will cause the next call to -measureWithSizeRange: to actually compute a new layout
// instead of returning the current layout
_calculatedLayout.dirty = YES;
// This will cause the next call to -layoutThatFits:parentSize: to compute a new layout instead of returning
// the cached layout in case the constrained or parent size did not change
_calculatedDisplayNodeLayout->invalidate();
}
- (void)__didLoad
@@ -2756,47 +2817,11 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
});
}
- (void)_applyPendingLayoutContext
{
ASDN::MutexLocker l(__instanceLock__);
if (_pendingLayoutTransition) {
[self _applyLayout:_pendingLayoutTransition.pendingLayout layoutTransition:_pendingLayoutTransition];
_pendingLayoutTransition = nil;
}
}
- (void)_applyLayout:(ASLayout *)layout layoutTransition:(ASLayoutTransition *)layoutTransition
{
ASDN::MutexLocker l(__instanceLock__);
_calculatedLayout = layout;
ASDisplayNodeAssertTrue(layout.layoutableObject == self);
ASDisplayNodeAssertTrue(layout.size.width >= 0.0);
ASDisplayNodeAssertTrue(layout.size.height >= 0.0);
if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) {
return;
}
// Trampoline to the main thread if necessary
if (ASDisplayNodeThreadIsMain() == NO && layoutTransition.isSynchronous == NO) {
// Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded
ASPerformBlockOnMainThread(^{
[layoutTransition commitTransition];
});
return;
}
[layoutTransition commitTransition];
}
- (void)layout
{
ASDisplayNodeAssertMainThread();
if ([self _hasDirtyLayout]) {
if (_calculatedDisplayNodeLayout->isDirty()) {
return;
}
@@ -2805,8 +2830,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
- (void)__layoutSublayouts
{
for (ASLayout *subnodeLayout in _calculatedLayout.sublayouts) {
((ASDisplayNode *)subnodeLayout.layoutableObject).frame = [subnodeLayout frame];
for (ASLayout *subnodeLayout in _calculatedDisplayNodeLayout->layout.sublayouts) {
((ASDisplayNode *)subnodeLayout.layoutable).frame = subnodeLayout.frame;
}
}
@@ -3205,6 +3230,19 @@ ASEnvironmentLayoutExtensibilityForwarding
}
}
#endif
#pragma mark - Deprecated
- (CGSize)measure:(CGSize)constrainedSize
{
return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
}
@end
@implementation ASDisplayNode (Debugging)

View File

@@ -14,10 +14,12 @@
#import "_ASDisplayLayer.h"
#import "ASAssert.h"
#import "ASDimension.h"
#import "ASDisplayNode+Subclasses.h"
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNodeExtras.h"
#import "ASDisplayNode+Beta.h"
#import "ASLayout.h"
#import "ASTextNode.h"
#import "ASImageNode+AnimatedImagePrivate.h"
@@ -186,13 +188,23 @@ struct ASImageNodeDrawParameters {
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
ASDN::MutexLocker l(__instanceLock__);
// if a preferredFrameSize is set, call the superclass to return that instead of using the image size.
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO)
return [super calculateSizeThatFits:constrainedSize];
else if (_image)
return _image.size;
else
return CGSizeZero;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// If a preferredFrameSize is set, call the superclass to return that instead of using the image size.
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) {
#if DEBUG
NSLog(@"Using -[ASDisplayNode preferredFrameSize] is deprecated.");
#endif
return self.preferredFrameSize;
}
#pragma clang diagnostic pop
if (_image == nil) {
return constrainedSize;
}
return _image.size;
}
#pragma mark - Setter / Getter
@@ -645,7 +657,7 @@ static ASDN::Mutex cacheLock;
if (_debugLabelNode) {
CGSize boundsSize = self.bounds.size;
CGSize debugLabelSize = [_debugLabelNode measure:boundsSize];
CGSize debugLabelSize = [_debugLabelNode layoutThatFits:ASSizeRangeMake(CGSizeZero, boundsSize)].size;
CGPoint debugLabelOrigin = CGPointMake(boundsSize.width - debugLabelSize.width,
boundsSize.height - debugLabelSize.height);
_debugLabelNode.frame = (CGRect) {debugLabelOrigin, debugLabelSize};

View File

@@ -392,21 +392,14 @@
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
CGSize size = self.preferredFrameSize;
if (CGSizeEqualToSize(size, CGSizeZero)) {
size = constrainedSize;
// FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc)
// These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value.
if (!isValidForLayout(size.width)) {
size.width = 100.0;
}
if (!isValidForLayout(size.height)) {
size.height = 100.0;
}
// FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc)
// These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value.
if (!ASIsCGSizeValidForLayout(constrainedSize)) {
//ASDisplayNodeAssert(NO, @"Invalid width or height in ASMapNode");
constrainedSize = CGSizeZero;
}
[self setSnapshotSizeWithReloadIfNeeded:size];
return size;
[self setSnapshotSizeWithReloadIfNeeded:constrainedSize];
return constrainedSize;
}
- (void)calculatedLayoutDidChange

View File

@@ -1130,7 +1130,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
} else {
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0),
CGSizeMake(_nodesConstrainedWidth, FLT_MAX));
CGSizeMake(_nodesConstrainedWidth, CGFLOAT_MAX));
}
return constrainedSize;
}
@@ -1186,7 +1186,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
// Also, in many cases, some nodes may not need to be re-measured at all, such as when user enters and then immediately leaves editing mode.
// To avoid premature optimization and making such assumption, as well as to keep ASTableView simple, re-measurement is strictly done on main.
[self beginUpdates];
CGSize calculatedSize = [[node measureWithSizeRange:constrainedSize] size];
const CGSize calculatedSize = [node layoutThatFits:constrainedSize].size;
node.frame = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height);
[self endUpdates];
}

View File

@@ -240,10 +240,17 @@ static NSString * const kRate = @"rate";
ASDN::MutexLocker l(__instanceLock__);
CGSize calculatedSize = constrainedSize;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// if a preferredFrameSize is set, call the superclass to return that instead of using the image size.
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO)
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) {
#if DEBUG
NSLog(@"Using -[ASDisplayNde preferredFrameSize] is deprecated.");
#endif
calculatedSize = self.preferredFrameSize;
}
#pragma clang diagnostic pop
// Prevent crashes through if infinite width or height
if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) {
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
@@ -251,8 +258,9 @@ static NSString * const kRate = @"rate";
}
if (_playerNode) {
_playerNode.preferredFrameSize = calculatedSize;
[_playerNode measure:calculatedSize];
_playerNode.width = ASDimensionMake(calculatedSize.width);
_playerNode.height = ASDimensionMake(calculatedSize.height);
[_playerNode layoutThatFits:ASSizeRangeMake(CGSizeZero, calculatedSize)];
}
return calculatedSize;

View File

@@ -324,7 +324,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
{
if (_playbackButtonNode == nil) {
_playbackButtonNode = [[ASDefaultPlaybackButton alloc] init];
_playbackButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
_playbackButtonNode.width = ASDimensionMakeWithPoints(16.0);
_playbackButtonNode.height = ASDimensionMakeWithPoints(22.0);
if (_delegateFlags.delegatePlaybackButtonTint) {
_playbackButtonNode.tintColor = [_delegate videoPlayerNodePlaybackButtonTint:self];
} else {
@@ -598,7 +600,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
return spinnnerView;
}];
_spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0);
_spinnerNode.width = ASDimensionMakeWithPoints(44.0);
_spinnerNode.height = ASDimensionMakeWithPoints(44.0);
[self addSubnode:_spinnerNode];
[self setNeedsLayout];
@@ -689,23 +693,21 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
return controls;
}
#pragma mark - Layout
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
CGSize maxSize = constrainedSize.max;
if (!CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero)) {
maxSize = self.preferredFrameSize;
}
// Prevent crashes through if infinite width or height
if (isinf(maxSize.width) || isinf(maxSize.height)) {
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoPlayerNode");
maxSize = CGSizeZero;
}
_videoNode.preferredFrameSize = maxSize;
_videoNode.size = ASLayoutableSizeMakeFromCGSize(maxSize);
ASLayoutSpec *layoutSpec;
if (_delegateFlags.delegateLayoutSpecForControls) {
layoutSpec = [_delegate videoPlayerNodeLayoutSpec:self forControls:_cachedControls forMaximumSize:maxSize];
} else {
@@ -716,21 +718,21 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
if (_spinnerNode) {
ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode];
centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
centerLayoutSpec.size = ASLayoutableSizeMakeFromCGSize(maxSize);
[children addObject:centerLayoutSpec];
}
ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec];
overlaySpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
overlaySpec.size = ASLayoutableSizeMakeFromCGSize(maxSize);
[children addObject:overlaySpec];
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
}
- (ASLayoutSpec*)defaultLayoutSpecThatFits:(CGSize)maxSize
- (ASLayoutSpec *)defaultLayoutSpecThatFits:(CGSize)maxSize
{
_scrubberNode.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
_scrubberNode.width = ASDimensionMakeWithPoints(maxSize.width);
_scrubberNode.height = ASDimensionMakeWithPoints(44.0);
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.flexGrow = YES;

View File

@@ -15,6 +15,7 @@
#import "ASAvailability.h"
#import "ASDisplayNodeInternal.h"
#import "ASDisplayNode+FrameworkPrivate.h"
#import "ASLayout.h"
#import "ASTraitCollection.h"
#import "ASEnvironmentInternal.h"
#import "ASRangeControllerUpdateRangeProtocol+Beta.h"
@@ -105,7 +106,7 @@
[self progagateNewEnvironmentTraitCollection:environmentTraitCollection];
}];
} else {
[_node measureWithSizeRange:[self nodeConstrainedSize]];
[_node layoutThatFits:[self nodeConstrainedSize]];
}
if (!AS_AT_LEAST_IOS9) {
@@ -132,7 +133,7 @@ ASVisibilityDidMoveToParentViewController;
// We do this early layout because we need to get any ASCollectionNodes etc. into the
// hierarchy before UIKit applies the scroll view inset adjustments, if you are using
// automatic subnode management.
[_node measureWithSizeRange:[self nodeConstrainedSize]];
[_node layoutThatFits:[self nodeConstrainedSize]];
[_node recursivelyFetchData];
@@ -302,7 +303,7 @@ ASVisibilityDepthImplementation;
// once we've propagated all the traits, layout this node.
// Remeasure the node with the latest constrained size old constrained size may be incorrect.
[self.node measureWithSizeRange:[self nodeConstrainedSize]];
[self.node layoutThatFits:[self nodeConstrainedSize]];
[self.node setNeedsLayout];
}
}

View File

@@ -15,6 +15,7 @@
#import "ASDisplayNodeExtras.h"
#import "ASWeakSet.h"
#import "UIImage+ASConvenience.h"
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
#import <AsyncDisplayKit/CGRect+ASConvenience.h>
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
@@ -612,7 +613,7 @@ static BOOL __shouldShowRangeDebugOverlay = NO;
[self setBarSubviewOrder];
CGRect rect = CGRectIntegral(CGRectMake(0, 0, boundsSize.width, floorf(boundsSize.height / 2.0)));
rect.size = [_debugText measure:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
rect.size = [_debugText layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))].size;
rect.origin.x = (boundsSize.width - rect.size.width) / 2.0;
_debugText.frame = rect;
rect.origin.y += rect.size.height;

View File

@@ -22,9 +22,9 @@
static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionView) {
CGSize maxSize = collectionView.bounds.size;
if (ASScrollDirectionContainsHorizontalDirection(collectionView.scrollableDirections)) {
maxSize.width = FLT_MAX;
maxSize.width = CGFLOAT_MAX;
} else {
maxSize.height = FLT_MAX;
maxSize.height = CGFLOAT_MAX;
}
return ASSizeRangeMake(CGSizeZero, maxSize);
}

View File

@@ -155,7 +155,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize
{
CGRect frame = CGRectZero;
frame.size = [node measureWithSizeRange:constrainedSize].size;
frame.size = [node layoutThatFits:constrainedSize].size;
node.frame = frame;
}

View File

@@ -12,7 +12,6 @@
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/ASRelativeSize.h>
@protocol ASEnvironment;
@class UITraitCollection;
@@ -40,12 +39,11 @@ typedef struct ASEnvironmentLayoutOptionsState {
CGFloat spacingAfter;// = 0;
BOOL flexGrow;// = NO;
BOOL flexShrink;// = NO;
ASRelativeDimension flexBasis;// = ASRelativeDimensionUnconstrained;
ASDimension flexBasis;// = ASRelativeDimensionAuto;
ASStackLayoutAlignSelf alignSelf;// = ASStackLayoutAlignSelfAuto;
CGFloat ascender;// = 0;
CGFloat descender;// = 0;
ASRelativeSizeRange sizeRange;// = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero));;
CGPoint layoutPosition;// = CGPointZero;
struct ASEnvironmentStateExtensions _extensions;

View File

@@ -8,7 +8,6 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIKit.h>

View File

@@ -39,26 +39,26 @@ static NSUInteger const kBackgroundChildIndex = 1;
}
/**
First layout the contents, then fit the background image.
* First layout the contents, then fit the background image.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize
{
ASLayout *contentsLayout = [[self child] measureWithSizeRange:constrainedSize];
ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize];
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2];
if (self.background) {
// Size background to exactly the same size.
ASLayout *backgroundLayout = [self.background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
ASLayout *backgroundLayout = [self.background layoutThatFits:ASSizeRangeMake(contentsLayout.size)
parentSize:parentSize];
backgroundLayout.position = CGPointZero;
[sublayouts addObject:backgroundLayout];
}
contentsLayout.position = CGPointZero;
[sublayouts addObject:contentsLayout];
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:contentsLayout.size
sublayouts:sublayouts];
return [ASLayout layoutWithLayoutable:self size:contentsLayout.size sublayouts:sublayouts];
}
- (void)setBackground:(id<ASLayoutable>)background

View File

@@ -11,58 +11,203 @@
#pragma once
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASAssert.h>
/** A dimension relative to constraints to be provided in the future. */
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) {
ASDISPLAYNODE_INLINE BOOL ASPointsAreValidForLayout(CGFloat points)
{
return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < (CGFLOAT_MAX / 2.0));
}
ASDISPLAYNODE_INLINE BOOL ASIsCGSizeValidForLayout(CGSize size)
{
return (ASPointsAreValidForLayout(size.width) && ASPointsAreValidForLayout(size.height));
}
/**
* A dimension relative to constraints to be provided in the future.
* A ASDimension can be one of three types:
*
* "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.
*
* "Points" - Just a number. It will always resolve to exactly this amount.
*
* "Percent" - Multiplied to a provided parent amount to resolve a final amount.
*/
typedef NS_ENUM(NSInteger, ASDimensionUnit) {
/** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */
ASDimensionUnitAuto,
/** Just a number. It will always resolve to exactly this amount. This is the default type. */
ASRelativeDimensionTypePoints,
ASDimensionUnitPoints,
/** Multiplied to a provided parent amount to resolve a final amount. */
ASRelativeDimensionTypeFraction,
ASDimensionUnitFraction,
};
typedef struct {
ASRelativeDimensionType type;
ASDimensionUnit unit;
CGFloat value;
} ASRelativeDimension;
} ASDimension;
/** Expresses an inclusive range of sizes. Used to provide a simple constraint to layout. */
/**
* Expresses an inclusive range of sizes. Used to provide a simple constraint to layout.
*/
typedef struct {
CGSize min;
CGSize max;
} ASSizeRange;
extern ASRelativeDimension const ASRelativeDimensionUnconstrained;
/**
* A struct specifying a ASLayoutable's size. Example:
*
* ASLayoutableSize size = (ASLayoutableSize){
* .width = ASDimensionMakeWithFraction(0.25),
* .maxWidth = ASDimensionMakeWithPoints(200),
* .minHeight = ASDimensionMakeWithFraction(0.50)
* };
*
* Description: <ASLayoutableSize: exact={25%, Auto}, min={Auto, 50%}, max={200pt, Auto}>
*
*/
typedef struct {
ASDimension width;
ASDimension height;
ASDimension minWidth;
ASDimension maxWidth;
ASDimension minHeight;
ASDimension maxHeight;
} ASLayoutableSize;
#define isValidForLayout(x) ((isnormal(x) || x == 0.0) && x >= 0.0 && x < (CGFLOAT_MAX / 2.0))
extern ASDimension const ASDimensionAuto;
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASRelativeDimension
extern ASRelativeDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value);
#pragma mark - ASDimension
extern ASRelativeDimension ASRelativeDimensionMakeWithPoints(CGFloat points);
/**
* Returns a dimension with the specified type and value.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value)
{
if (unit == ASDimensionUnitPoints) {
ASDisplayNodeCAssertPositiveReal(@"Points", value);
} else if (unit == ASDimensionUnitFraction) {
// TODO: Enable this assertion for 2.0. Check that there is no use case for using a larger value, e.g. to layout for a clipsToBounds = NO element.
// ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", value);
}
ASDimension dimension;
dimension.unit = unit;
dimension.value = value;
return dimension;
}
extern ASRelativeDimension ASRelativeDimensionMakeWithFraction(CGFloat fraction);
/**
* Returns a dimension with the specified points value.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(CGFloat points)
{
return ASDimensionMake(ASDimensionUnitPoints, points);
}
extern ASRelativeDimension ASRelativeDimensionCopy(ASRelativeDimension aDimension);
/**
* Returns a dimension by parsing the specified dimension string.
* Examples: ASDimensionMake(@"0.5%") = ASDimensionMake(ASDimensionUnitFraction, 0.5)
* ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5)
*/
ASOVERLOADABLE extern ASDimension ASDimensionMake(NSString *dimension);
extern BOOL ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimension lhs, ASRelativeDimension rhs);
/**
* Returns a dimension with the specified points value.
*/
ASDISPLAYNODE_INLINE ASDimension ASDimensionMakeWithPoints(CGFloat points)
{
ASDisplayNodeCAssertPositiveReal(@"Points", points);
return ASDimensionMake(ASDimensionUnitPoints, points);
}
extern NSString *NSStringFromASRelativeDimension(ASRelativeDimension dimension);
/**
* Returns a dimension with the specified fraction value.
*/
ASDISPLAYNODE_INLINE ASDimension ASDimensionMakeWithFraction(CGFloat fraction)
{
ASDisplayNodeCAssert( 0 <= fraction && fraction <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", fraction);
return ASDimensionMake(ASDimensionUnitFraction, fraction);
}
/**
* Returns whether two dimensions are equal.
*/
ASDISPLAYNODE_INLINE BOOL ASDimensionEqualToDimension(ASDimension lhs, ASDimension rhs)
{
return (lhs.unit == rhs.unit && lhs.value == rhs.value);
}
/**
* Returns a NSString representation of a dimension.
*/
extern NSString *NSStringFromASDimension(ASDimension dimension);
/**
* Resolve this dimension to a parent size.
*/
ASDISPLAYNODE_INLINE CGFloat ASDimensionResolve(ASDimension dimension, CGFloat parentSize, CGFloat autoSize)
{
switch (dimension.unit) {
case ASDimensionUnitAuto:
return autoSize;
case ASDimensionUnitPoints:
return dimension.value;
case ASDimensionUnitFraction:
return dimension.value * parentSize;
}
}
#pragma mark - NSNumber+ASDimension
@interface NSNumber (ASDimension)
@property (nonatomic, readonly) ASDimension as_pointDimension;
@property (nonatomic, readonly) ASDimension as_fractionDimension;
@end
extern CGFloat ASRelativeDimensionResolve(ASRelativeDimension dimension, CGFloat parent);
#pragma mark - ASSizeRange
extern ASSizeRange ASSizeRangeMake(CGSize min, CGSize max);
/**
* Creates an ASSizeRange with provided min and max size.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
{
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height);
ASDisplayNodeCAssert(min.width <= max.width,
@"Range min width (%f) must not be larger than max width (%f).", min.width, max.width);
ASDisplayNodeCAssert(min.height <= max.height,
@"Range min height (%f) must not be larger than max height (%f).", min.height, max.height);
ASSizeRange sizeRange;
sizeRange.min = min;
sizeRange.max = max;
return sizeRange;
}
/** Creates an ASSizeRange with the provided size as both min and max */
extern ASSizeRange ASSizeRangeMakeExactSize(CGSize size);
/**
* Creates an ASSizeRange with provided size as both min and max.
*/
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASSizeRange ASSizeRangeMake(CGSize exactSize)
{
return ASSizeRangeMake(exactSize, exactSize);
}
/** Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange. */
extern CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size);
/**
* Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange.
*/
ASDISPLAYNODE_INLINE CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size)
{
return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)),
MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height)));
}
/**
* Intersects another size range. If the other size range does not overlap in either dimension, this size range
@@ -70,9 +215,91 @@ extern CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size);
*/
extern ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange);
extern BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs);
/**
* Returns whether two size ranges are equal in min and max size
*/
ASDISPLAYNODE_INLINE BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs)
{
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
}
/**
* Returns a string representation of a size range
*/
extern NSString *NSStringFromASSizeRange(ASSizeRange sizeRange);
#pragma mark - ASLayoutableSize
/**
* Returns an ASLayoutableSize with default values.
*/
ASDISPLAYNODE_INLINE ASLayoutableSize ASLayoutableSizeMake()
{
return (ASLayoutableSize){
.width = ASDimensionAuto,
.height = ASDimensionAuto,
.minWidth = ASDimensionAuto,
.maxWidth = ASDimensionAuto,
.minHeight = ASDimensionAuto,
.maxHeight = ASDimensionAuto
};
}
/**
* Returns an ASLayoutableSize with the specified CGSize values as width and height.
*/
ASDISPLAYNODE_INLINE ASLayoutableSize ASLayoutableSizeMakeFromCGSize(CGSize size)
{
ASLayoutableSize s = ASLayoutableSizeMake();
s.width = ASDimensionMakeWithPoints(size.width);
s.height = ASDimensionMakeWithPoints(size.height);
return s;
}
/**
* Returns whether two sizes are equal.
*/
ASDISPLAYNODE_INLINE BOOL ASLayoutableSizeEqualToLayoutableSize(ASLayoutableSize lhs, ASLayoutableSize rhs)
{
return (ASDimensionEqualToDimension(lhs.width, rhs.width)
&& ASDimensionEqualToDimension(lhs.height, rhs.height)
&& ASDimensionEqualToDimension(lhs.minWidth, rhs.minWidth)
&& ASDimensionEqualToDimension(lhs.maxWidth, rhs.maxWidth)
&& ASDimensionEqualToDimension(lhs.minHeight, rhs.minHeight)
&& ASDimensionEqualToDimension(lhs.maxHeight, rhs.maxHeight));
}
/**
* Returns a string formatted to contain the data from an ASLayoutableSize.
*/
extern NSString *NSStringFromASLayoutableSize(ASLayoutableSize size);
/**
* Resolve the given size relative to a parent size and an auto size.
* From the given size uses width, height to resolve the exact size constraint, uses the minHeight and minWidth to
* resolve the min size constraint and the maxHeight and maxWidth to resolve the max size constraint. For every
* dimension with unit ASDimensionUnitAuto the given autoASSizeRange value will be used.
* Based on the calculated exact, min and max size constraints the final size range will be calculated.
*/
extern ASSizeRange ASLayoutableSizeResolveAutoSize(ASLayoutableSize size, const CGSize parentSize, ASSizeRange autoASSizeRange);
/**
* Resolve the given size to a parent size. Uses internally ASLayoutableSizeResolveAutoSize with {INFINITY, INFINITY} as
* as autoASSizeRange. For more information look at ASLayoutableSizeResolveAutoSize.
*/
ASDISPLAYNODE_INLINE ASSizeRange ASLayoutableSizeResolve(ASLayoutableSize size, const CGSize parentSize)
{
return ASLayoutableSizeResolveAutoSize(size, parentSize, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));
}
#pragma mark - Deprecated
/**
* Function is deprecated. Use ASSizeRangeMakeWithExactCGSize instead.
*/
extern ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED;
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END

View File

@@ -11,89 +11,164 @@
#import "ASDimension.h"
#import "ASAssert.h"
ASRelativeDimension const ASRelativeDimensionUnconstrained = {};
#pragma mark - ASDimension
#pragma mark - ASRelativeDimension
ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0};
ASRelativeDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value)
ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension)
{
if (type == ASRelativeDimensionTypePoints) {
ASDisplayNodeCAssertPositiveReal(@"Points", value);
} else if (type == ASRelativeDimensionTypeFraction) {
// TODO: Enable this assertion for 2.0. Check that there is no use case for using a larger value, e.g. to layout for a clipsToBounds = NO element.
// ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASRelativeDimension fraction value (%f) must be between 0 and 1.", value);
// Handle empty string
if (dimension.length == 0) {
return ASDimensionMake(ASDimensionUnitPoints, 0.0);
}
ASRelativeDimension dimension; dimension.type = type; dimension.value = value; return dimension;
// Handle points
NSUInteger pointsStringLocation = [dimension rangeOfString:@"pt"].location;
if (pointsStringLocation != NSNotFound) {
// Check if points is at the end and remove it
if (pointsStringLocation == (dimension.length-2)) {
dimension = [dimension substringToIndex:(dimension.length-2)];
return ASDimensionMake(ASDimensionUnitPoints, dimension.floatValue);
}
}
// Handle fraction
NSUInteger percentStringLocation = [dimension rangeOfString:@"%"].location;
if (percentStringLocation != NSNotFound) {
// Check if percent is at the end and remove it
if (percentStringLocation == (dimension.length-1)) {
dimension = [dimension substringToIndex:(dimension.length-1)];
return ASDimensionMake(ASDimensionUnitFraction, dimension.floatValue);
}
}
// Assert as parsing went wrong
ASDisplayNodeCAssert(NO, @"Parsing dimension failed for: %@", dimension);
return ASDimensionAuto;
}
ASRelativeDimension ASRelativeDimensionMakeWithPoints(CGFloat points)
NSString *NSStringFromASDimension(ASDimension dimension)
{
ASDisplayNodeCAssertPositiveReal(@"Points", points);
return ASRelativeDimensionMake(ASRelativeDimensionTypePoints, points);
}
ASRelativeDimension ASRelativeDimensionMakeWithFraction(CGFloat fraction)
{
// ASDisplayNodeCAssert( 0 <= fraction && fraction <= 1.0, @"ASRelativeDimension fraction value (%f) must be between 0 and 1.", fraction);
return ASRelativeDimensionMake(ASRelativeDimensionTypeFraction, fraction);
}
ASRelativeDimension ASRelativeDimensionCopy(ASRelativeDimension aDimension)
{
return ASRelativeDimensionMake(aDimension.type, aDimension.value);
}
BOOL ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimension lhs, ASRelativeDimension rhs)
{
return lhs.type == rhs.type && lhs.value == rhs.value;
}
NSString *NSStringFromASRelativeDimension(ASRelativeDimension dimension)
{
switch (dimension.type) {
case ASRelativeDimensionTypePoints:
switch (dimension.unit) {
case ASDimensionUnitPoints:
return [NSString stringWithFormat:@"%.0fpt", dimension.value];
case ASRelativeDimensionTypeFraction:
case ASDimensionUnitFraction:
return [NSString stringWithFormat:@"%.0f%%", dimension.value * 100.0];
case ASDimensionUnitAuto:
return @"Auto";
}
}
CGFloat ASRelativeDimensionResolve(ASRelativeDimension dimension, CGFloat parent)
#pragma mark - NSNumber+ASDimension
@implementation NSNumber (ASDimension)
- (ASDimension)as_pointDimension
{
switch (dimension.type) {
case ASRelativeDimensionTypePoints:
return dimension.value;
case ASRelativeDimensionTypeFraction:
return dimension.value * parent;
}
return ASDimensionMake(ASDimensionUnitPoints, self.floatValue);
}
- (ASDimension)as_fractionDimension
{
return ASDimensionMake(ASDimensionUnitFraction, self.floatValue);
}
@end
#pragma mark - ASRelativeSize
/**
* Expresses a size with relative dimensions. Only used for calculations internally in ASDimension.h
*/
typedef struct {
ASDimension width;
ASDimension height;
} ASRelativeSize;
ASDISPLAYNODE_INLINE ASRelativeSize ASRelativeSizeMake(ASDimension width, ASDimension height)
{
ASRelativeSize size;
size.width = width;
size.height = height;
return size;
}
// ** Resolve this relative size relative to a parent size. */
ASDISPLAYNODE_INLINE CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize, CGSize autoSize)
{
return CGSizeMake(ASDimensionResolve(relativeSize.width, parentSize.width, autoSize.width),
ASDimensionResolve(relativeSize.height, parentSize.height, autoSize.height));
}
// ** Returns a string formatted to contain the data from an ASRelativeSize. */
ASDISPLAYNODE_INLINE NSString *NSStringFromASRelativeSize(ASRelativeSize size)
{
return [NSString stringWithFormat:@"{%@, %@}",
NSStringFromASDimension(size.width),
NSStringFromASDimension(size.height)];
}
#pragma mark - ASLayoutableSize
NSString *NSStringFromASLayoutableSize(ASLayoutableSize size)
{
return [NSString stringWithFormat:
@"<ASLayoutableSize: exact=%@, min=%@, max=%@>",
NSStringFromASRelativeSize(ASRelativeSizeMake(size.width, size.height)),
NSStringFromASRelativeSize(ASRelativeSizeMake(size.minWidth, size.minHeight)),
NSStringFromASRelativeSize(ASRelativeSizeMake(size.maxWidth, size.maxHeight))];
}
ASDISPLAYNODE_INLINE void ASLayoutableSizeConstrain(CGFloat minVal, CGFloat exactVal, CGFloat maxVal, CGFloat *outMin, CGFloat *outMax)
{
NSCAssert(!isnan(minVal), @"minVal must not be NaN");
NSCAssert(!isnan(maxVal), @"maxVal must not be NaN");
// Avoid use of min/max primitives since they're harder to reason
// about in the presence of NaN (in exactVal)
// Follow CSS: min overrides max overrides exact.
// Begin with the min/max range
*outMin = minVal;
*outMax = maxVal;
if (maxVal <= minVal) {
// min overrides max and exactVal is irrelevant
*outMax = minVal;
return;
}
if (isnan(exactVal)) {
// no exact value, so leave as a min/max range
return;
}
if (exactVal > maxVal) {
// clip to max value
*outMin = maxVal;
} else if (exactVal < minVal) {
// clip to min value
*outMax = minVal;
} else {
// use exact value
*outMin = *outMax = exactVal;
}
}
ASSizeRange ASLayoutableSizeResolveAutoSize(ASLayoutableSize size, const CGSize parentSize, ASSizeRange autoASSizeRange)
{
CGSize resolvedExact = ASRelativeSizeResolveSize(ASRelativeSizeMake(size.width, size.height), parentSize, {NAN, NAN});
CGSize resolvedMin = ASRelativeSizeResolveSize(ASRelativeSizeMake(size.minWidth, size.minHeight), parentSize, autoASSizeRange.min);
CGSize resolvedMax = ASRelativeSizeResolveSize(ASRelativeSizeMake(size.maxWidth, size.maxHeight), parentSize, autoASSizeRange.max);
CGSize rangeMin, rangeMax;
ASLayoutableSizeConstrain(resolvedMin.width, resolvedExact.width, resolvedMax.width, &rangeMin.width, &rangeMax.width);
ASLayoutableSizeConstrain(resolvedMin.height, resolvedExact.height, resolvedMax.height, &rangeMin.height, &rangeMax.height);
return {rangeMin, rangeMax};
}
#pragma mark - ASSizeRange
ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
{
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width);
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height);
ASDisplayNodeCAssert(min.width <= max.width,
@"Range min width (%f) must not be larger than max width (%f).", min.width, max.width);
ASDisplayNodeCAssert(min.height <= max.height,
@"Range min height (%f) must not be larger than max height (%f).", min.height, max.height);
ASSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
{
return ASSizeRangeMake(size, size);
}
CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size)
{
return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)),
MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height)));
}
struct _Range {
CGFloat min;
CGFloat max;
@@ -126,14 +201,17 @@ ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRan
return {{w.min, h.min}, {w.max, h.max}};
}
BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs)
{
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
}
NSString * NSStringFromASSizeRange(ASSizeRange sizeRange)
NSString *NSStringFromASSizeRange(ASSizeRange sizeRange)
{
return [NSString stringWithFormat:@"<ASSizeRange: min=%@, max=%@>",
NSStringFromCGSize(sizeRange.min),
NSStringFromCGSize(sizeRange.max)];
}
#pragma mark - Deprecated
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
{
return ASSizeRangeMake(size);
}

View File

@@ -67,8 +67,15 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
Inset will compute a new constrained size for it's child after applying insets and re-positioning
the child to respect the inset.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize
{
if (self.child == nil) {
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing.");
return [ASLayout layoutWithLayoutable:self size:CGSizeZero];
}
const CGFloat insetsX = (finiteOrZero(_insets.left) + finiteOrZero(_insets.right));
const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom));
@@ -88,14 +95,12 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
}
};
if (self.child == nil) {
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing.");
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:CGSizeZero];
}
const CGSize insetParentSize = {
MAX(0, parentSize.width - insetsX),
MAX(0, parentSize.height - insetsY)
};
ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize];
ASLayout *sublayout = [self.child layoutThatFits:insetConstrainedSize parentSize:insetParentSize];
const CGSize computedSize = ASSizeRangeClamp(constrainedSize, {
finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width),
@@ -113,10 +118,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
sublayout.position = CGPointMake(x, y);
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:computedSize
sublayouts:@[sublayout]];
return [ASLayout layoutWithLayoutable:self size:computedSize sublayouts:@[sublayout]];
}
@end

View File

@@ -17,10 +17,29 @@
NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
extern CGPoint const CGPointNull;
extern BOOL CGPointIsNull(CGPoint point);
/**
* Safely calculates the layout of the given root layoutable by guarding against nil nodes.
* @param rootLayoutable The root node to calculate the layout for.
* @param sizeRange The size range to calculate the root layout within.
*/
extern ASLayout *ASCalculateRootLayout(id<ASLayoutable> rootLayoutable, const ASSizeRange sizeRange);
/**
* Safely computes the layout of the given node by guarding against nil nodes.
* @param component The component to calculate the layout for.
* @param sizeRange The size range to calculate the node layout within.
* @param parentSize The parent size of the node to calculate the layout for.
*/
extern ASLayout *ASCalculateLayout(id<ASLayoutable> layoutable, const ASSizeRange sizeRange, const CGSize parentSize);
ASDISPLAYNODE_EXTERN_C_END
/**
* A node in the layout tree that represents the size and position of the object that created it (ASLayoutable).
*/
@@ -29,68 +48,56 @@ extern BOOL CGPointIsNull(CGPoint point);
/**
* The underlying object described by this layout
*/
@property (nonatomic, weak, readonly) id<ASLayoutable> layoutableObject;
@property (nonatomic, weak, readonly) id<ASLayoutable> layoutable;
/**
* The type of ASLayoutable that created this layout
*/
@property (nonatomic, readonly) ASLayoutableType type;
@property (nonatomic, assign, readonly) ASLayoutableType type;
/**
* Size of the current layout
*/
@property (nonatomic, readonly) CGSize size;
@property (nonatomic, assign, readonly) CGSize size;
/**
* Position in parent. Default to CGPointNull.
*
* @discussion When being used as a sublayout, this property must not equal CGPointNull.
*/
@property (nonatomic, readwrite) CGPoint position;
/**
* The size range that was use to determine the size of the layout.
*/
@property (nonatomic, readonly) ASSizeRange constrainedSizeRange;
@property (nonatomic, assign, readwrite) CGPoint position;
/**
* Array of ASLayouts. Each must have a valid non-null position.
*/
@property (nonatomic, readonly) NSArray<ASLayout *> *sublayouts;
/**
* Mark the layout dirty for future regeneration.
*/
@property (nonatomic, getter=isDirty) BOOL dirty;
@property (nonatomic, copy, readonly) NSArray<ASLayout *> *sublayouts;
/**
* @abstract Returns a valid frame for the current layout computed with the size and position.
* @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite.
*/
@property (nonatomic, readonly) CGRect frame;
@property (nonatomic, assign, readonly) CGRect frame;
/**
* Designated initializer
*/
- (instancetype)initWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
position:(CGPoint)position
sublayouts:(NSArray *)sublayouts NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts NS_DESIGNATED_INITIALIZER;
/**
* Convenience class initializer for layout construction.
*
* @param layoutableObject The backing ASLayoutable object.
* @param layoutable The backing ASLayoutable object.
* @param size The size of this layout.
* @param position The position of this layout within its parent (if available).
* @param sublayouts Sublayouts belong to the new layout.
*/
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
/**
* Convenience initializer that has CGPointNull position.
@@ -98,43 +105,29 @@ extern BOOL CGPointIsNull(CGPoint point);
* or for ASLayoutSpec subclasses that are referencing the "self" level in the layout tree,
* or for creating a sublayout of which the position is yet to be determined.
*
* @param layoutableObject The backing ASLayoutable object.
* @param size The size of this layout.
* @param sublayouts Sublayouts belong to the new layout.
* @param layoutable The backing ASLayoutable object.
* @param size The size of this layout.
* @param sublayouts Sublayouts belong to the new layout.
*/
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
/**
* Convenience that has CGPointNull position and no sublayouts.
* Convenience that has CGPointNull position and no sublayouts.
* Best used for creating a layout that has no sublayouts, and is either a root one
* or a sublayout of which the position is yet to be determined.
*
* @param layoutableObject The backing ASLayoutable object.
* @param size The size of this layout.
* @param layoutable The backing ASLayoutable object.
* @param size The size of this layout.
*/
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size;
/**
* Convenience initializer that is flattened and has CGPointNull position.
*
* @param layoutableObject The backing ASLayoutable object.
* @param size The size of this layout.
* @param sublayouts Sublayouts belong to the new layout.
*/
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size;
/**
* Convenience initializer that creates a layout based on the values of the given layout, with a new position
* @param layout The layout to use to create the new layout
* @param position The position of the new layout
*
* @param layout The layout to use to create the new layout
* @param position The position of the new layout
*/
+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position;
@@ -145,6 +138,14 @@ extern BOOL CGPointIsNull(CGPoint point);
@end
@interface ASLayout (Unavailable)
- (instancetype)init __unavailable;
@end
#pragma mark - Debugging
@interface ASLayout (Debugging)
/**
@@ -154,10 +155,4 @@ extern BOOL CGPointIsNull(CGPoint point);
@end
@interface ASLayout (Unavailable)
- (instancetype)init __unavailable;
@end
NS_ASSUME_NONNULL_END

View File

@@ -51,38 +51,37 @@ static inline NSString * descriptionIndents(NSUInteger indents)
@dynamic frame, type;
- (instancetype)initWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
position:(CGPoint)position
sublayouts:(NSArray *)sublayouts
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
{
NSParameterAssert(layoutable);
self = [super init];
if (self) {
NSParameterAssert(layoutableObject);
#if DEBUG
for (ASLayout *sublayout in sublayouts) {
ASDisplayNodeAssert(CGPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout.");
}
#endif
_layoutableObject = layoutableObject;
_layoutable = layoutable;
if (!isValidForLayout(size.width) || !isValidForLayout(size.height)) {
ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Production will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutableObject);
if (!ASIsCGSizeValidForLayout(size)) {
ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutable);
size = CGSizeZero;
} else {
size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
}
_constrainedSizeRange = sizeRange;
_size = size;
_dirty = NO;
if (CGPointIsNull(position) == NO) {
_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y));
} else {
_position = position;
}
_sublayouts = sublayouts != nil ? [sublayouts copy] : @[];
_flattened = NO;
}
@@ -97,61 +96,41 @@ static inline NSString * descriptionIndents(NSUInteger indents)
#pragma mark - Class Constructors
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
position:(CGPoint)position
sublayouts:(NSArray *)sublayouts
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size
position:(CGPoint)position
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
{
return [[self alloc] initWithLayoutableObject:layoutableObject
constrainedSizeRange:sizeRange
size:size
position:position
sublayouts:sublayouts];
}
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
sublayouts:(NSArray *)sublayouts
{
return [self layoutWithLayoutableObject:layoutableObject
constrainedSizeRange:sizeRange
return [[self alloc] initWithLayoutable:layoutable
size:size
position:CGPointNull
position:position
sublayouts:sublayouts];
}
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
{
return [self layoutWithLayoutableObject:layoutableObject
constrainedSizeRange:sizeRange
size:size
position:CGPointNull
sublayouts:nil];
return [self layoutWithLayoutable:layoutable
size:size
position:CGPointNull
sublayouts:sublayouts];
}
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable size:(CGSize)size
{
return [self layoutWithLayoutableObject:layoutableObject
constrainedSizeRange:sizeRange
size:size
position:CGPointNull
sublayouts:sublayouts];
return [self layoutWithLayoutable:layoutable
size:size
position:CGPointNull
sublayouts:nil];
}
+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position
{
return [self layoutWithLayoutableObject:layout.layoutableObject
constrainedSizeRange:layout.constrainedSizeRange
size:layout.size
position:position
sublayouts:layout.sublayouts];
return [self layoutWithLayoutable:layout.layoutable
size:layout.size
position:position
sublayouts:layout.sublayouts];
}
#pragma mark - Layout Flattening
@@ -186,17 +165,14 @@ static inline NSString * descriptionIndents(NSUInteger indents)
}
}
return [ASLayout layoutWithLayoutableObject:_layoutableObject
constrainedSizeRange:_constrainedSizeRange
size:_size
sublayouts:flattenedSublayouts];
return [ASLayout layoutWithLayoutable:_layoutable size:_size sublayouts:flattenedSublayouts];
}
#pragma mark - Accessors
- (ASLayoutableType)type
{
return _layoutableObject.layoutableType;
return _layoutable.layoutableType;
}
- (CGRect)frame
@@ -231,8 +207,8 @@ static inline NSString * descriptionIndents(NSUInteger indents)
- (NSString *)description
{
return [NSString stringWithFormat:@"<<ASLayout: %p>, layoutable = %@, position = %@; size = %@; constrainedSizeRange = %@>",
self, self.layoutableObject, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size), NSStringFromASSizeRange(self.constrainedSizeRange)];
return [NSString stringWithFormat:@"<<ASLayout: %p>, layoutable = %@, position = %@; size = %@;>",
self, self.layoutable, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size)/*, NSStringFromASSizeRange(self.constrainedSize)*/];
}
- (NSString *)recursiveDescription
@@ -253,3 +229,18 @@ static inline NSString * descriptionIndents(NSUInteger indents)
}
@end
ASLayout *ASCalculateLayout(id<ASLayoutable> layoutable, const ASSizeRange sizeRange, const CGSize parentSize)
{
ASDisplayNodeCAssertNotNil(layoutable, @"Not valid layoutable passed in.");
return [layoutable layoutThatFits:sizeRange parentSize:parentSize];
}
ASLayout *ASCalculateRootLayout(id<ASLayoutable> rootLayoutable, const ASSizeRange sizeRange)
{
ASLayout *layout = ASCalculateLayout(rootLayoutable, sizeRange, sizeRange.max);
// Here could specific verfication happen
return layout;
}

View File

@@ -13,7 +13,9 @@
NS_ASSUME_NONNULL_BEGIN
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
/**
* A layout spec is an immutable object that describes a layout, loosely inspired by React.
*/
@interface ASLayoutSpec : NSObject <ASLayoutable>
/**
@@ -88,6 +90,28 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
* An ASLayoutSpec subclass that can wrap a ASLayoutable and calculates the layout of the child.
*/
@interface ASWrapperLayoutSpec : ASLayoutSpec
/*
* Returns an ASWrapperLayoutSpec object with the given layoutable as child
*/
+ (instancetype)wrapperWithLayoutable:(id<ASLayoutable>)layoutable;
/*
* Returns an ASWrapperLayoutSpec object initialized with the given layoutable as child
*/
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable NS_DESIGNATED_INITIALIZER;;
/*
* Init not available for ASWrapperLayoutSpec
*/
- (instancetype)init __unavailable;
@end
@interface ASLayoutSpec (Debugging) <ASLayoutableAsciiArtProtocol>
/**
* Used by other layout specs to create ascii art debug strings

View File

@@ -11,6 +11,7 @@
#import "ASLayoutSpec.h"
#import "ASAssert.h"
#import "ASInternalHelpers.h"
#import "ASEnvironmentInternal.h"
#import "ASLayout.h"
@@ -24,25 +25,46 @@
typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASChildMap;
@interface ASLayoutSpec() {
ASEnvironmentState _environmentState;
ASDN::RecursiveMutex __instanceLock__;
ASLayoutableSize _size;
ASEnvironmentState _environmentState;
ASChildMap _children;
}
@end
@implementation ASLayoutSpec
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis,
alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType;
// Dynamic properties for ASLayoutables
@dynamic layoutableType, size;
// Dynamic properties for sizing
@dynamic width, height, minWidth, maxWidth, minHeight, maxHeight;
// Dynamic properties for stack spec
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender;
// Dynamic properties for static spec
@dynamic layoutPosition;
@synthesize isFinalLayoutable = _isFinalLayoutable;
#pragma mark - Class
+ (void)initialize
{
[super initialize];
if (self != [ASLayoutSpec class]) {
ASDisplayNodeAssert(!ASSubclassOverridesSelector([ASLayoutSpec class], self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead overwrite calculateLayoutThatFits:", NSStringFromClass(self));
}
}
#pragma mark - Lifecycle
- (instancetype)init
{
if (!(self = [super init])) {
return nil;
}
_isMutable = YES;
_size = ASLayoutableSizeMake();
_environmentState = ASEnvironmentStateMakeDefault();
return self;
}
@@ -57,13 +79,54 @@ typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASCh
return YES;
}
#pragma mark - Sizing
- (ASLayoutableSize)size
{
ASDN::MutexLocker l(__instanceLock__);
return _size;
}
- (void)setSize:(ASLayoutableSize)size
{
ASDN::MutexLocker l(__instanceLock__);
_size = size;
}
ASLayoutableSizeForwarding
ASLayoutableSizeHelperForwarding
#pragma mark - Layout
// Deprecated
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:constrainedSize.min];
return [self layoutThatFits:constrainedSize];
}
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
{
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
}
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
{
return [self calculateLayoutThatFits:constrainedSize restrictedToSize:_size relativeToParentSize:parentSize];
}
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize
{
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutableSizeResolve(_size, parentSize));
return [self calculateLayoutThatFits:resolvedRange];
}
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
return [ASLayout layoutWithLayoutable:self size:constrainedSize.min];
}
- (id<ASLayoutable>)finalLayoutable
@@ -233,6 +296,37 @@ ASEnvironmentLayoutExtensibilityForwarding
@end
#pragma mark - ASWrapperLayoutSpec
@implementation ASWrapperLayoutSpec
+ (instancetype)wrapperWithLayoutable:(id<ASLayoutable>)layoutable
{
return [[self alloc] initWithLayoutable:layoutable];
}
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
{
self = [super init];
if (self) {
self.child = layoutable;
}
return self;
}
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
ASLayout *sublayout = [self.child layoutThatFits:constrainedSize parentSize:constrainedSize.max];
sublayout.position = CGPointZero;
return [ASLayout layoutWithLayoutable:self size:sublayout.size sublayouts:@[sublayout]];
}
@end
#pragma mark - ASLayoutSpec (Debugging)
@implementation ASLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol

View File

@@ -65,28 +65,14 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
- (void)validateLayout:(ASLayout *)layout
{
for (ASLayout *sublayout in layout.sublayouts) {
id<ASLayoutable> layoutable = layout.layoutableObject;
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutableObject;
id<ASLayoutable> layoutable = layout.layoutable;
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutable;
NSString *assertMessage = nil;
Class stackContainerClass = [ASStaticLayoutSpec class];
// Check for default sizeRange and layoutPosition
ASRelativeSizeRange sizeRange = sublayoutLayoutable.sizeRange;
ASRelativeSizeRange zeroSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeZero);
// Currently setting the preferredFrameSize also updates the sizeRange. Create a size range based on the
// preferredFrameSize and check it if it's the same as the current sizeRange to be sure it was not changed manually
CGSize preferredFrameSize = CGSizeZero;
if ([sublayoutLayoutable respondsToSelector:@selector(preferredFrameSize)]) {
preferredFrameSize = [((ASDisplayNode *)sublayoutLayoutable) preferredFrameSize];
}
ASRelativeSizeRange preferredFrameSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(preferredFrameSize);
if (ASRelativeSizeRangeEqualToRelativeSizeRange(sizeRange, zeroSizeRange) == NO &&
ASRelativeSizeRangeEqualToRelativeSizeRange(sizeRange, preferredFrameSizeRange) == NO) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(sizeRange), sublayoutLayoutable, stackContainerClass);
} else if (!CGPointEqualToPoint(sublayoutLayoutable.layoutPosition, CGPointZero)) {
// Check for default layoutPosition
if (!CGPointEqualToPoint(sublayoutLayoutable.layoutPosition, CGPointZero)) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(layoutPosition), sublayoutLayoutable, stackContainerClass);
}
@@ -110,9 +96,9 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
- (void)validateLayout:(ASLayout *)layout
{
id<ASLayoutable> layoutable = layout.layoutableObject;
id<ASLayoutable> layoutable = layout.layoutable;
for (ASLayout *sublayout in layout.sublayouts) {
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutableObject;
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutable;
NSString *assertMessage = nil;
Class stackContainerClass = [ASStackLayoutSpec class];
@@ -126,7 +112,7 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexGrow), sublayoutLayoutable, stackContainerClass);
} else if (sublayoutLayoutable.flexShrink == YES) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexShrink), sublayoutLayoutable, stackContainerClass);
} else if (!ASRelativeDimensionEqualToRelativeDimension(sublayoutLayoutable.flexBasis, ASRelativeDimensionUnconstrained) ) {
} else if (!ASDimensionEqualToDimension(sublayoutLayoutable.flexBasis, ASDimensionAuto) ) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexBasis), sublayoutLayoutable, stackContainerClass);
} else if (sublayoutLayoutable.alignSelf != ASStackLayoutAlignSelfAuto) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(alignSelf), sublayoutLayoutable, stackContainerClass);

View File

@@ -9,7 +9,6 @@
//
#import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASRelativeSize.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/ASStackLayoutable.h>
#import <AsyncDisplayKit/ASStaticLayoutable.h>
@@ -21,6 +20,13 @@
@class ASLayout;
@class ASLayoutSpec;
/** A constant that indicates that the parent's size is not yet determined in a given dimension. */
extern CGFloat const ASLayoutableParentDimensionUndefined;
/** A constant that indicates that the parent's size is not yet determined in either dimension. */
extern CGSize const ASLayoutableParentSizeUndefined;
/** Type of ASLayoutable */
typedef NS_ENUM(NSUInteger, ASLayoutableType) {
ASLayoutableTypeLayoutSpec,
ASLayoutableTypeDisplayNode
@@ -49,24 +55,142 @@ NS_ASSUME_NONNULL_BEGIN
/**
* @abstract Returns type of layoutable
*/
@property (nonatomic, readonly) ASLayoutableType layoutableType;
@property (nonatomic, assign, readonly) ASLayoutableType layoutableType;
/**
* @abstract Returns if the layoutable can be used to layout in an asynchronous way on a background thread.
*/
@property (nonatomic, readonly) BOOL canLayoutAsynchronous;
@property (nonatomic, assign, readonly) BOOL canLayoutAsynchronous;
#pragma mark - Sizing
/**
* @abstract Calculate a layout based on given size range.
* @abstract The width property specifies the height of the content area of an ASLayoutable.
* The minWidth and maxWidth properties override width.
* Defaults to ASRelativeDimensionTypeAuto
*/
@property (nonatomic, assign, readwrite) ASDimension width;
/**
* @abstract The height property specifies the height of the content area of an ASLayoutable
* The minHeight and maxHeight properties override height.
* Defaults to ASDimensionTypeAuto
*/
@property (nonatomic, assign, readwrite) ASDimension height;
/**
* @abstract The minHeight property is used to set the minimum height of a given element. It prevents the used value
* of the height property from becoming smaller than the value specified for minHeight.
* The value of minHeight overrides both maxHeight and height.
* Defaults to ASDimensionTypeAuto
*/
@property (nonatomic, assign, readwrite) ASDimension minHeight;
/**
* @abstract The maxHeight property is used to set the maximum height of an element. It prevents the used value of the
* height property from becoming larger than the value specified for maxHeight.
* The value of maxHeight overrides height, but minHeight overrides maxHeight.
* Defaults to ASDimensionTypeAuto
*/
@property (nonatomic, assign, readwrite) ASDimension maxHeight;
/**
* @abstract The minWidth property is used to set the minimum width of a given element. It prevents the used value of
* the width property from becoming smaller than the value specified for minWidth.
* The value of minWidth overrides both maxWidth and width.
* Defaults to ASDimensionTypeAuto
*/
@property (nonatomic, assign, readwrite) ASDimension minWidth;
/**
* @abstract The maxWidth property is used to set the maximum width of a given element. It prevents the used value of
* the width property from becoming larger than the value specified for maxWidth.
* The value of maxWidth overrides width, but minWidth overrides maxWidth.
* Defaults to ASDimensionTypeAuto
*/
@property (nonatomic, assign, readwrite) ASDimension maxWidth;
/**
* @abstract Set max and width properties from given size
*/
- (void)setSizeWithCGSize:(CGSize)size;
/**
* @abstract Set minHeight, maxHeight and minWidth, maxWidth properties from given size
*/
- (void)setExactSizeWithCGSize:(CGSize)size;
#pragma mark - Calculate layout
/**
* @abstract Asks the node to return a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver and its children.
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may
* be expensive if result is not cached.
*
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;
/**
* Call this on children layoutables to compute their layouts within your implementation of -calculateLayoutThatFits:.
*
* @warning You may not override this method. Override -calculateLayoutThatFits: instead.
* @warning In almost all cases, prefer the use of ASCalculateLayout in ASLayout
*
* @param constrainedSize Specifies a minimum and maximum size. The receiver must choose a size that is in this range.
* @param parentSize The parent node's size. If the parent component does not have a final size in a given dimension,
* then it should be passed as ASLayoutableParentDimensionUndefined (for example, if the parent's width
* depends on the child's size).
*
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
* constraint and the result.
*
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
*/
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize;
/**
* Override this method to compute your layoutable's layout.
*
* @discussion Why do you need to override -calculateLayoutThatFits: instead of -layoutThatFits:parentSize:?
* The base implementation of -layoutThatFits:parentSize: does the following for you:
* 1. First, it uses the parentSize parameter to resolve the nodes's size (the one assigned to the size property).
* 2. Then, it intersects the resolved size with the constrainedSize parameter. If the two don't intersect,
* constrainedSize wins. This allows a component to always override its childrens' sizes when computing its layout.
* (The analogy for UIView: you might return a certain size from -sizeThatFits:, but a parent view can always override
* that size and set your frame to any size.)
* 3. It caches it result for reuse
*
* @param constrainedSize A min and max size. This is computed as described in the description. The ASLayout you
* return MUST have a size between these two sizes.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
/**
* In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the
* layoutable's size, parentSize, and constrained size. With these values you could calculate the final constrained size
* and call -calculateLayoutThatFits: with the result.
*
* @warning Overriding this method should be done VERY rarely.
*/
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize;
#pragma mark - Layout options from the Layoutable Protocols
#pragma mark - ASStackLayoutable
/**
* @abstract Additional space to place before this object in the stacking direction.
@@ -94,10 +218,10 @@ NS_ASSUME_NONNULL_BEGIN
/**
* @abstract Specifies the initial size in the stack dimension for this object.
* Default to ASRelativeDimensionUnconstrained.
* Default to ASRelativeDimensionAuto
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
@property (nonatomic, readwrite) ASDimension flexBasis;
/**
* @abstract Orientation of the object along cross axis, overriding alignItems
@@ -115,15 +239,28 @@ NS_ASSUME_NONNULL_BEGIN
*/
@property (nonatomic, readwrite) CGFloat descender;
#pragma mark - ASStaticLayoutable
/**
If specified, the child's size is restricted according to this size. Fractions are resolved relative to the static layout spec.
*/
@property (nonatomic, assign) ASRelativeSizeRange sizeRange;
/** The position of this object within its parent spec. */
#pragma mark - ASStaticLayoutable
/**
* @abstract The position of this object within its parent spec.
*/
@property (nonatomic, assign) CGPoint layoutPosition;
#pragma mark - Deprecated
/**
* @abstract Calculate a layout based on given size range.
*
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
*
* @return An ASLayout instance defining the layout of the receiver and its children.
*
* @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout or ASCalculateLayout instead
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED;
@end
NS_ASSUME_NONNULL_END

View File

@@ -16,6 +16,9 @@
#import <map>
CGFloat const ASLayoutableParentDimensionUndefined = NAN;
CGSize const ASLayoutableParentSizeUndefined = {ASLayoutableParentDimensionUndefined, ASLayoutableParentDimensionUndefined};
int32_t const ASLayoutableContextInvalidTransitionID = 0;
int32_t const ASLayoutableContextDefaultTransitionID = ASLayoutableContextInvalidTransitionID + 1;

View File

@@ -8,7 +8,7 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import <Foundation/Foundation.h>
#import "ASDimension.h"
@class ASLayoutSpec;
@protocol ASLayoutable;
@@ -40,6 +40,11 @@ extern void ASLayoutableClearCurrentContext();
*/
@protocol ASLayoutablePrivate <NSObject>
/**
* @abstract A size constraint that should apply to this ASLayoutable.
*/
@property (nonatomic, assign, readwrite) ASLayoutableSize size;
/**
* @abstract This method can be used to give the user a chance to wrap an ASLayoutable in an ASLayoutSpec
* just before it is added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always
@@ -61,9 +66,7 @@ extern void ASLayoutableClearCurrentContext();
@end
#pragma mark - ASLayoutOptionsForwarding
#pragma mark - ASLayoutableForwarding
/**
* Both an ASDisplayNode and an ASLayoutSpec conform to ASLayoutable. There are several properties
* in ASLayoutable that are used when a node or spec is used in a layout spec.
@@ -76,6 +79,103 @@ extern void ASLayoutableClearCurrentContext();
* layoutSpec may require.
*/
#pragma mark - ASLayoutableSizeForwarding
#define ASLayoutableSizeForwarding \
\
- (ASDimension)width\
{\
ASDN::MutexLocker l(__instanceLock__);\
return _size.width;\
}\
\
- (void)setWidth:(ASDimension)width\
{\
ASDN::MutexLocker l(__instanceLock__);\
_size.width = width;\
}\
\
- (ASDimension)height\
{\
ASDN::MutexLocker l(__instanceLock__);\
return _size.height;\
}\
\
- (void)setHeight:(ASDimension)height\
{\
ASDN::MutexLocker l(__instanceLock__);\
_size.height = height;\
}\
\
- (ASDimension)minWidth\
{\
ASDN::MutexLocker l(__instanceLock__);\
return _size.minWidth;\
}\
\
- (void)setMinWidth:(ASDimension)minWidth\
{\
ASDN::MutexLocker l(__instanceLock__);\
_size.minWidth = minWidth;\
}\
\
- (ASDimension)maxWidth\
{\
ASDN::MutexLocker l(__instanceLock__);\
return _size.maxWidth;\
}\
\
- (void)setMaxWidth:(ASDimension)maxWidth\
{\
ASDN::MutexLocker l(__instanceLock__);\
_size.maxWidth = maxWidth;\
}\
\
- (ASDimension)minHeight\
{\
ASDN::MutexLocker l(__instanceLock__);\
return _size.minHeight;\
}\
\
- (void)setMinHeight:(ASDimension)minHeight\
{\
ASDN::MutexLocker l(__instanceLock__);\
_size.minHeight = minHeight;\
}\
\
- (ASDimension)maxHeight\
{\
ASDN::MutexLocker l(__instanceLock__);\
return _size.maxHeight;\
}\
\
- (void)setMaxHeight:(ASDimension)maxHeight\
{\
ASDN::MutexLocker l(__instanceLock__);\
_size.maxHeight = maxHeight;\
}\
#pragma mark - ASLayoutableSizeHelperForwarding
#define ASLayoutableSizeHelperForwarding \
- (void)setSizeWithCGSize:(CGSize)size\
{\
self.width = ASDimensionMakeWithPoints(size.width);\
self.height = ASDimensionMakeWithPoints(size.height);\
}\
\
- (void)setExactSizeWithCGSize:(CGSize)size\
{\
self.minWidth = ASDimensionMakeWithPoints(size.width);\
self.minHeight = ASDimensionMakeWithPoints(size.height);\
self.maxWidth = ASDimensionMakeWithPoints(size.width);\
self.maxHeight = ASDimensionMakeWithPoints(size.height);\
}\
#pragma mark - ASLayoutOptionsForwarding
#define ASEnvironmentLayoutOptionsForwarding \
- (void)propagateUpLayoutOptionsState\
{\
@@ -132,12 +232,12 @@ extern void ASLayoutableClearCurrentContext();
[self propagateUpLayoutOptionsState];\
}\
\
- (ASRelativeDimension)flexBasis\
- (ASDimension)flexBasis\
{\
return _environmentState.layoutOptionsState.flexBasis;\
}\
\
- (void)setFlexBasis:(ASRelativeDimension)flexBasis\
- (void)setFlexBasis:(ASDimension)flexBasis\
{\
_environmentState.layoutOptionsState.flexBasis = flexBasis;\
[self propagateUpLayoutOptionsState];\
@@ -176,17 +276,6 @@ extern void ASLayoutableClearCurrentContext();
[self propagateUpLayoutOptionsState];\
}\
\
- (ASRelativeSizeRange)sizeRange\
{\
return _environmentState.layoutOptionsState.sizeRange;\
}\
\
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\
{\
_environmentState.layoutOptionsState.sizeRange = sizeRange;\
[self propagateUpLayoutOptionsState];\
}\
\
- (CGPoint)layoutPosition\
{\
return _environmentState.layoutOptionsState.layoutPosition;\

View File

@@ -47,21 +47,21 @@ static NSUInteger const kOverlayChildIndex = 1;
/**
First layout the contents, then fit the overlay on top of it.
*/
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
restrictedToSize:(ASLayoutableSize)size
relativeToParentSize:(CGSize)parentSize
{
ASLayout *contentsLayout = [self.child measureWithSizeRange:constrainedSize];
ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize];
contentsLayout.position = CGPointZero;
NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout];
if (self.overlay) {
ASLayout *overlayLayout = [self.overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
ASLayout *overlayLayout = [self.overlay layoutThatFits:ASSizeRangeMake(contentsLayout.size)
parentSize:contentsLayout.size];
overlayLayout.position = CGPointZero;
[sublayouts addObject:overlayLayout];
}
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:contentsLayout.size
sublayouts:sublayouts];
return [ASLayout layoutWithLayoutable:self size:contentsLayout.size sublayouts:sublayouts];
}
@end

View File

@@ -47,16 +47,18 @@
_ratio = ratio;
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
std::vector<CGSize> sizeOptions;
if (!isinf(constrainedSize.max.width)) {
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
if (!isinf(constrainedSize.max.width) && ASPointsAreValidForLayout(constrainedSize.max.width)) {
sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
constrainedSize.max.width,
ASFloorPixelValue(_ratio * constrainedSize.max.width)
}));
}
if (!isinf(constrainedSize.max.height)) {
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
if (!isinf(constrainedSize.max.height) && ASPointsAreValidForLayout(constrainedSize.max.width)) {
sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
ASFloorPixelValue(constrainedSize.max.height / _ratio),
constrainedSize.max.height
@@ -70,12 +72,10 @@
// If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through.
const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize);
ASLayout *sublayout = [self.child measureWithSizeRange:childRange];
const CGSize parentSize = (bestSize == sizeOptions.end()) ? ASLayoutableParentSizeUndefined : *bestSize;
ASLayout *sublayout = [self.child layoutThatFits:childRange parentSize:parentSize];
sublayout.position = CGPointZero;
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:sublayout.size
sublayouts:@[sublayout]];
return [ASLayout layoutWithLayoutable:self size:sublayout.size sublayouts:@[sublayout]];
}
@end

View File

@@ -52,11 +52,15 @@
_sizingOption = sizingOption;
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
// If we have a finite size in any direction, pass this so that the child can
// resolve percentages against it. Otherwise pass ASLayoutableParentDimensionUndefined
// as the size will depend on the content
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
CGSize size = {
constrainedSize.max.width,
constrainedSize.max.height
isinf(constrainedSize.max.width) || !ASPointsAreValidForLayout(constrainedSize.max.width) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.width,
isinf(constrainedSize.max.height) || !ASPointsAreValidForLayout(constrainedSize.max.height) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.height
};
BOOL reduceWidth = (_horizontalPosition & ASRelativeLayoutSpecPositionCenter) != 0 ||
@@ -70,7 +74,8 @@
reduceWidth ? 0 : constrainedSize.min.width,
reduceHeight ? 0 : constrainedSize.min.height,
};
ASLayout *sublayout = [self.child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)];
ASLayout *sublayout = [self.child layoutThatFits:ASSizeRangeMake(minChildSize, constrainedSize.max) parentSize:size];
// If we have an undetermined height or width, use the child size to define the layout
// size
@@ -94,10 +99,7 @@
ASRoundPixelValue((size.height - sublayout.size.height) * yPosition)
};
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:size
sublayouts:@[sublayout]];
return [ASLayout layoutWithLayoutable:self size:size sublayouts:@[sublayout]];
}
- (CGFloat)proportionOfAxisForAxisPosition:(ASRelativeLayoutSpecPosition)position

View File

@@ -8,68 +8,4 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASDimension.h>
/**
Expresses a size with relative dimensions.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASRelativeDimension width;
ASRelativeDimension height;
} ASRelativeSize;
/**
Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout.
Used by ASStaticLayoutSpec.
*/
typedef struct {
ASRelativeSize min;
ASRelativeSize max;
} ASRelativeSizeRange;
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_BEGIN
#pragma mark - ASRelativeSize
extern ASRelativeSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height);
/** Convenience constructor to provide size in points. */
extern ASRelativeSize ASRelativeSizeMakeWithCGSize(CGSize size);
/** Convenience constructor to provide size as a fraction. */
extern ASRelativeSize ASRelativeSizeMakeWithFraction(CGFloat fraction);
/** Resolve this relative size relative to a parent size. */
extern CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize);
extern BOOL ASRelativeSizeEqualToRelativeSize(ASRelativeSize lhs, ASRelativeSize rhs);
extern NSString *NSStringFromASRelativeSize(ASRelativeSize size);
#pragma mark - ASRelativeSizeRange
extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASRelativeSize min, ASRelativeSize max);
#pragma mark Convenience constructors to provide an exact size (min == max).
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSize exact);
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact);
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction);
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth,
ASRelativeDimension exactHeight);
extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs);
/** Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange. */
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, CGSize parentSize);
NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END
// TODO: layout: Remove file

View File

@@ -8,84 +8,4 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import "ASRelativeSize.h"
ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {};
#pragma mark - ASRelativeSize
ASRelativeSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height)
{
ASRelativeSize size; size.width = width; size.height = height; return size;
}
ASRelativeSize ASRelativeSizeMakeWithCGSize(CGSize size)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width),
ASRelativeDimensionMakeWithPoints(size.height));
}
ASRelativeSize ASRelativeSizeMakeWithFraction(CGFloat fraction)
{
return ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(fraction),
ASRelativeDimensionMakeWithFraction(fraction));
}
CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize)
{
return CGSizeMake(ASRelativeDimensionResolve(relativeSize.width, parentSize.width),
ASRelativeDimensionResolve(relativeSize.height, parentSize.height));
}
BOOL ASRelativeSizeEqualToRelativeSize(ASRelativeSize lhs, ASRelativeSize rhs)
{
return ASRelativeDimensionEqualToRelativeDimension(lhs.width, rhs.width)
&& ASRelativeDimensionEqualToRelativeDimension(lhs.height, rhs.height);
}
NSString *NSStringFromASRelativeSize(ASRelativeSize size)
{
return [NSString stringWithFormat:@"{%@, %@}",
NSStringFromASRelativeDimension(size.width),
NSStringFromASRelativeDimension(size.height)];
}
#pragma mark - ASRelativeSizeRange
ASRelativeSizeRange ASRelativeSizeRangeMake(ASRelativeSize min, ASRelativeSize max)
{
ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSize exact)
{
return ASRelativeSizeRangeMake(exact, exact);
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithFraction(fraction));
}
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth,
ASRelativeDimension exactHeight)
{
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight));
}
BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs)
{
return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max);
}
ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
CGSize parentSize)
{
return ASSizeRangeMake(ASRelativeSizeResolveSize(relativeSizeRange.min, parentSize),
ASRelativeSizeResolveSize(relativeSizeRange.max, parentSize));
}
// TODO: layout: Remove file

View File

@@ -118,7 +118,7 @@
_baselineRelativeArrangement = baselineRelativeArrangement;
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
std::vector<id<ASLayoutable>> stackChildren;
for (id<ASLayoutable> child in self.children) {
@@ -126,9 +126,7 @@
}
if (stackChildren.empty()) {
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:constrainedSize.min];
return [ASLayout layoutWithLayoutable:self size:constrainedSize.min];
}
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement};
@@ -161,10 +159,7 @@
sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()];
}
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:ASSizeRangeClamp(constrainedSize, finalSize)
sublayouts:sublayouts];
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts];
}
- (void)resolveHorizontalAlignment

View File

@@ -44,10 +44,10 @@ NS_ASSUME_NONNULL_BEGIN
/**
* @abstract Specifies the initial size in the stack dimension for this object.
* Default to ASRelativeDimensionUnconstrained.
* Default to ASDimensionAuto
* Used when attached to a stack layout.
*/
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
@property (nonatomic, readwrite) ASDimension flexBasis;
/**
* @abstract Orientation of the object along cross axis, overriding alignItems

View File

@@ -9,7 +9,6 @@
//
#import <AsyncDisplayKit/ASLayoutSpec.h>
#import <AsyncDisplayKit/ASRelativeSize.h>
NS_ASSUME_NONNULL_BEGIN

View File

@@ -34,42 +34,46 @@
return self;
}
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
CGSize maxConstrainedSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height);
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
CGSize size = {
(isinf(constrainedSize.max.width) || !ASPointsAreValidForLayout(constrainedSize.max.width)) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.width,
(isinf(constrainedSize.max.height) || !ASPointsAreValidForLayout(constrainedSize.max.height)) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.height
};
NSArray *children = self.children;
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
for (id<ASLayoutable> child in children) {
CGPoint layoutPosition = child.layoutPosition;
CGSize autoMaxSize = CGSizeMake(maxConstrainedSize.width - layoutPosition.x,
maxConstrainedSize.height - layoutPosition.y);
CGSize autoMaxSize = {
constrainedSize.max.width - layoutPosition.x,
constrainedSize.max.height - layoutPosition.y
};
const ASSizeRange childConstraint = ASLayoutableSizeResolveAutoSize(child.size, size, {{0,0}, autoMaxSize});
ASRelativeSizeRange childSizeRange = child.sizeRange;
BOOL childIsUnconstrained = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, childSizeRange);
ASSizeRange childConstraint = childIsUnconstrained ? ASSizeRangeMake({0, 0}, autoMaxSize)
: ASRelativeSizeRangeResolve(childSizeRange, maxConstrainedSize);
ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
ASLayout *sublayout = [child layoutThatFits:childConstraint parentSize:size];
sublayout.position = layoutPosition;
[sublayouts addObject:sublayout];
}
CGSize size = CGSizeMake(constrainedSize.min.width, constrainedSize.min.height);
for (ASLayout *sublayout in sublayouts) {
CGPoint sublayoutPosition = sublayout.position;
CGSize sublayoutSize = sublayout.size;
size.width = MAX(size.width, sublayoutPosition.x + sublayoutSize.width);
size.height = MAX(size.height, sublayoutPosition.y + sublayoutSize.height);
if (isnan(size.width)) {
size.width = constrainedSize.min.width;
for (ASLayout *sublayout in sublayouts) {
size.width = MAX(size.width, sublayout.position.x + sublayout.size.width);
}
}
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:ASSizeRangeClamp(constrainedSize, size)
sublayouts:sublayouts];
if (isnan(size.height)) {
size.height = constrainedSize.min.height;
for (ASLayout *sublayout in sublayouts) {
size.height = MAX(size.height, sublayout.position.y + sublayout.size.height);
}
}
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:sublayouts];
}
@end

View File

@@ -8,8 +8,6 @@
// of patent rights can be found in the PATENTS file in the same directory.
//
#import <AsyncDisplayKit/ASRelativeSize.h>
NS_ASSUME_NONNULL_BEGIN
/**
@@ -18,11 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
@protocol ASStaticLayoutable
/**
If specified, the child's size is restricted according to this size. Fractions are resolved relative to the static layout spec.
* @abstract The position of this object within its parent spec.
*/
@property (nonatomic, assign) ASRelativeSizeRange sizeRange;
/** The position of this object within its parent spec. */
@property (nonatomic, assign) CGPoint layoutPosition;
@end

View File

@@ -43,8 +43,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4
};
@class _ASDisplayNodePosition;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp;
@@ -104,6 +102,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
@protected
ASDisplayNode * __weak _supernode;
ASLayoutableSize _size;
CGSize _preferredFrameSize;
ASSentinel *_displaySentinel;
@@ -114,21 +115,20 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
CGFloat _contentsScaleForDisplay;
ASEnvironmentState _environmentState;
ASLayout *_calculatedLayout;
UIEdgeInsets _hitTestSlop;
NSMutableArray *_subnodes;
// Main thread only
_ASTransitionContext *_pendingLayoutTransitionContext;
BOOL _automaticallyManagesSubnodes;
_ASTransitionContext *_pendingLayoutTransitionContext;
NSTimeInterval _defaultLayoutTransitionDuration;
NSTimeInterval _defaultLayoutTransitionDelay;
UIViewAnimationOptions _defaultLayoutTransitionOptions;
int32_t _pendingTransitionID;
ASLayoutTransition *_pendingLayoutTransition;
std::shared_ptr<ASDisplayNodeLayout> _calculatedDisplayNodeLayout;
ASDisplayNodeViewBlock _viewBlock;
ASDisplayNodeLayerBlock _layerBlock;
@@ -184,7 +184,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
// Swizzle to extend the builtin functionality with custom logic
- (BOOL)__shouldLoadViewOrLayer;
- (BOOL)__shouldSize;
/**
Invoked before a call to setNeedsLayout to the underlying view

View File

@@ -0,0 +1,58 @@
//
// ASDisplayNodeLayout.h
// AsyncDisplayKit
//
// Created by Michael Schneider on 08/26/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#pragma once
#import "ASDimension.h"
@class ASLayout;
/*
* Represents a connection between an ASLayout and a ASDisplayNode
* ASDisplayNode uses this to store additional information that are necessary besides the layout
*/
struct ASDisplayNodeLayout {
ASLayout *layout;
ASSizeRange constrainedSize;
CGSize parentSize;
BOOL _dirty;
/*
* Create a new display node layout with
* @param layout The layout to associate, usually returned from a call to -layoutThatFits:parentSize:
* @param constrainedSize Constrained size used to create the layout
* @param parentSize Parent size used to create the layout
*/
ASDisplayNodeLayout(ASLayout *layout, ASSizeRange constrainedSize, CGSize parentSize)
: layout(layout), constrainedSize(constrainedSize), parentSize(parentSize), _dirty(NO) {};
/*
* Creates a layout without any layout associated. By default this display node layout is dirty.
*/
ASDisplayNodeLayout()
: layout(nil), constrainedSize({{0, 0}, {0, 0}}), parentSize({0, 0}), _dirty(YES) {};
/**
* Returns if the display node layout is dirty as it was invalidated or it was created without a layout.
*/
BOOL isDirty();
/**
* Returns if ASDisplayNode is still valid for a given constrained and parent size
*/
BOOL isValidForConstrainedSizeParentSize(ASSizeRange constrainedSize, CGSize parentSize);
/**
* Invalidate the display node layout
*/
void invalidate();
};

View File

@@ -0,0 +1,34 @@
//
// ASDisplayNodeLayout.mm
// AsyncDisplayKit
//
// Created by Michael Schneider on 08/26/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
#include "ASDisplayNodeLayout.h"
BOOL ASDisplayNodeLayout::isDirty()
{
return _dirty || layout == nil;
}
BOOL ASDisplayNodeLayout::isValidForConstrainedSizeParentSize(ASSizeRange theConstrainedSize, CGSize theParentSize)
{
// Only generate a new layout if:
// - The current layout is dirty
// - The passed constrained size is different than the original layout's parent or constrained size
return (layout != nil
&& _dirty == NO
&& CGSizeEqualToSize(parentSize, theParentSize)
&& ASSizeRangeEqualToSizeRange(constrainedSize, theConstrainedSize));
}
void ASDisplayNodeLayout::invalidate()
{
_dirty = YES;
}

View File

@@ -144,7 +144,7 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme
if (parentLayoutOptionsState.flexGrow == defaultState.flexGrow) {
parentLayoutOptionsState.flexGrow = layoutOptionsState.flexGrow;
}
if (ASRelativeDimensionEqualToRelativeDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) {
if (ASDimensionEqualToDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) {
parentLayoutOptionsState.flexBasis = layoutOptionsState.flexBasis;
}
if (parentLayoutOptionsState.alignSelf == defaultState.alignSelf) {
@@ -154,10 +154,6 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme
parentLayoutOptionsState.ascender = layoutOptionsState.ascender;
}
if (ASRelativeSizeRangeEqualToRelativeSizeRange(parentLayoutOptionsState.sizeRange, defaultState.sizeRange)) {
// For now it is unclear if we should be up-propagating sizeRange or layoutPosition.
// parentLayoutOptionsState.sizeRange = layoutOptionsState.sizeRange;
}
if (CGPointEqualToPoint(parentLayoutOptionsState.layoutPosition, defaultState.layoutPosition)) {
// For now it is unclear if we should be up-propagating sizeRange or layoutPosition.
// parentLayoutOptionsState.layoutPosition = layoutOptionsState.layoutPosition;

View File

@@ -9,7 +9,6 @@
//
#import <UIKit/UIKit.h>
#import <CoreGraphics/CGBase.h>
#import <AsyncDisplayKit/ASBaseDefines.h>

View File

@@ -12,9 +12,13 @@
#import "ASDimension.h"
#import "_ASTransitionContext.h"
#import "ASDisplayNodeLayout.h"
#import <memory>
NS_ASSUME_NONNULL_BEGIN
@class ASDisplayNode;
@class ASLayout;
@interface ASLayoutTransition : NSObject <_ASTransitionContextLayoutDelegate>
@@ -26,12 +30,12 @@
/**
* Previous layout to transition from
*/
@property (nonatomic, readonly, strong) ASLayout *previousLayout;
@property (nonatomic, readonly, assign) std::shared_ptr<ASDisplayNodeLayout> previousLayout;
/**
* Pending layout to transition to
*/
@property (nonatomic, readonly, strong) ASLayout *pendingLayout;
@property (nonatomic, readonly, assign) std::shared_ptr<ASDisplayNodeLayout> pendingLayout;
/**
* Returns if the layout transition needs to happen synchronously
@@ -41,8 +45,9 @@
/**
* Returns a newly initialized layout transition
*/
- (instancetype)initWithNode:(ASDisplayNode *)node pendingLayout:(ASLayout *)pendingLayout previousLayout:(ASLayout *)previousLayout NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithNode:(ASDisplayNode *)node
pendingLayout:(std::shared_ptr<ASDisplayNodeLayout>)pendingLayout
previousLayout:(std::shared_ptr<ASDisplayNodeLayout>)previousLayout NS_DESIGNATED_INITIALIZER;
/**
* Insert and remove subnodes that where added or removed between the previousLayout and the pendingLayout
@@ -60,3 +65,11 @@
- (void)applySubnodeRemovals;
@end
@interface ASLayoutTransition (Unavailable)
- (instancetype)init __unavailable;
@end
NS_ASSUME_NONNULL_END

View File

@@ -34,7 +34,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
layout = queue.front();
queue.pop();
if (layout.layoutableObject.canLayoutAsynchronous == NO) {
if (layout.layoutable.canLayoutAsynchronous == NO) {
return NO;
}
@@ -58,8 +58,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
}
- (instancetype)initWithNode:(ASDisplayNode *)node
pendingLayout:(ASLayout *)pendingLayout
previousLayout:(ASLayout *)previousLayout
pendingLayout:(std::shared_ptr<ASDisplayNodeLayout>)pendingLayout
previousLayout:(std::shared_ptr<ASDisplayNodeLayout>)previousLayout
{
self = [super init];
if (self) {
@@ -72,10 +72,16 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
return self;
}
- (instancetype)init
{
ASDisplayNodeAssert(NO, @"Use the designated initializer");
return [self init];
}
- (BOOL)isSynchronous
{
ASDN::MutexSharedLocker l(__instanceLock__);
return !ASLayoutCanTransitionAsynchronous(_pendingLayout);
return !ASLayoutCanTransitionAsynchronous(_pendingLayout->layout);
}
- (void)commitTransition
@@ -112,23 +118,27 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
if (_calculatedSubnodeOperations) {
return;
}
if (_previousLayout) {
ASLayout *previousLayout = _previousLayout->layout;
ASLayout *pendingLayout = _pendingLayout->layout;
if (previousLayout) {
NSIndexSet *insertions, *deletions;
[_previousLayout.sublayouts asdk_diffWithArray:_pendingLayout.sublayouts
insertions:&insertions
deletions:&deletions
compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) {
return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject);
}];
findNodesInLayoutAtIndexes(_pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions);
findNodesInLayoutAtIndexesWithFilteredNodes(_previousLayout,
deletions,
_insertedSubnodes,
&_removedSubnodes,
&_removedSubnodePositions);
[previousLayout.sublayouts asdk_diffWithArray:pendingLayout.sublayouts
insertions:&insertions
deletions:&deletions
compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) {
return ASObjectIsEqual(lhs.layoutable, rhs.layoutable);
}];
findNodesInLayoutAtIndexes(pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions);
findNodesInLayoutAtIndexesWithFilteredNodes(previousLayout,
deletions,
_insertedSubnodes,
&_removedSubnodes,
&_removedSubnodePositions);
} else {
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_pendingLayout.sublayouts count])];
findNodesInLayoutAtIndexes(_pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions);
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pendingLayout.sublayouts count])];
findNodesInLayoutAtIndexes(pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions);
_removedSubnodes = nil;
}
_calculatedSubnodeOperations = YES;
@@ -160,9 +170,9 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
{
ASDN::MutexSharedLocker l(__instanceLock__);
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
return _previousLayout;
return _previousLayout->layout;
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
return _pendingLayout;
return _pendingLayout->layout;
} else {
return nil;
}
@@ -172,9 +182,9 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
{
ASDN::MutexSharedLocker l(__instanceLock__);
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
return _previousLayout.constrainedSizeRange;
return _previousLayout->constrainedSize;
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
return _pendingLayout.constrainedSizeRange;
return _pendingLayout->constrainedSize;
} else {
return ASSizeRangeMake(CGSizeZero, CGSizeZero);
}
@@ -212,7 +222,7 @@ static inline void findNodesInLayoutAtIndexesWithFilteredNodes(ASLayout *layout,
for (ASLayout *sublayout in layout.sublayouts) {
if (idx > lastIndex) { break; }
if (idx >= firstIndex && [indexes containsIndex:idx]) {
ASDisplayNode *node = (ASDisplayNode *)sublayout.layoutableObject;
ASDisplayNode *node = (ASDisplayNode *)sublayout.layoutable;
ASDisplayNodeCAssert(node, @"A flattened layout must consist exclusively of node sublayouts");
// Ignore the odd case in which a non-node sublayout is accessed and the type cast fails
if (node != nil) {

View File

@@ -15,7 +15,7 @@
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
const ASLayout *layout) {
__weak id<ASLayoutable> child = layout.layoutableObject;
__weak id<ASLayoutable> child = layout.layoutable;
switch (style.alignItems) {
case ASStackLayoutAlignItemsBaselineFirst:
return child.ascender;
@@ -33,7 +33,7 @@ static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
const CGFloat maxBaseline)
{
if (style.direction == ASStackLayoutDirectionHorizontal) {
__weak id<ASLayoutable> child = l.layoutableObject;
__weak id<ASLayoutable> child = l.layoutable;
switch (style.alignItems) {
case ASStackLayoutAlignItemsBaselineFirst:
return maxAscender - child.ascender;
@@ -89,9 +89,9 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
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 a.layoutableObject.ascender < b.layoutableObject.ascender;
return a.layoutable.ascender < b.layoutable.ascender;
});
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender;
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutable.ascender;
/*
Step 3: Take each child and update its layout position based on the baseline offset.
@@ -108,7 +108,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
CGPoint p = CGPointZero;
BOOL first = YES;
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
__weak id<ASLayoutable> child = l.layoutableObject;
__weak id<ASLayoutable> child = l.layoutable;
p = p + directionPoint(style.direction, child.spacingBefore, 0);
if (first) {
// if this is the first item use the previously computed start point
@@ -161,7 +161,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
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 : (*descenderIt).layoutableObject.descender;
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutable.descender;
return {stackedChildren, crossSize, maxAscender, minDescender};
}

View File

@@ -23,15 +23,16 @@ static ASLayout *crossChildLayout(const id<ASLayoutable> child,
const CGFloat stackMin,
const CGFloat stackMax,
const CGFloat crossMin,
const CGFloat crossMax)
const CGFloat crossMax,
const CGSize size)
{
const ASStackLayoutAlignItems alignItems = alignment(child.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);
ASLayout *layout = [child measureWithSizeRange:childSizeRange];
ASLayout *layout = [child layoutThatFits:childSizeRange parentSize:size];
ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child);
return layout ? : [ASLayout layoutWithLayoutableObject:child constrainedSizeRange:childSizeRange size:CGSizeZero];
return layout ? : [ASLayout layoutWithLayoutable:child size:{0, 0}];
}
/**
@@ -67,7 +68,8 @@ static ASLayout *crossChildLayout(const id<ASLayoutable> child,
@param style the layout style of the overall stack layout
*/
static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedItem> &layouts,
const ASStackLayoutSpecStyle &style)
const ASStackLayoutSpecStyle &style,
const CGSize size)
{
// Find the maximum cross dimension size among child layouts
const auto it = std::max_element(layouts.begin(), layouts.end(),
@@ -77,19 +79,16 @@ static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedIt
const CGFloat childCrossMax = it == layouts.end() ? 0 : crossDimension(style.direction, it->layout.size);
for (auto &l : layouts) {
const id<ASLayoutable> child = l.child;
const CGSize size = l.layout.size;
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems);
const CGFloat cross = crossDimension(style.direction, size);
const CGFloat stack = stackDimension(style.direction, size);
const CGFloat cross = crossDimension(style.direction, l.layout.size);
const CGFloat stack = stackDimension(style.direction, l.layout.size);
// restretch all stretchable children along the cross axis using the new min. set their max size to childCrossMax,
// not crossMax, so that if any of them would choose a larger size just because the min size increased (weird!)
// they are forced to choose the same width as all the other children.
if (alignItems == ASStackLayoutAlignItemsStretch && std::fabs(cross - childCrossMax) > 0.01) {
l.layout = crossChildLayout(child, style, stack, stack, childCrossMax, childCrossMax);
l.layout = crossChildLayout(l.child, style, stack, stack, childCrossMax, childCrossMax, size);
}
}
}
@@ -217,7 +216,8 @@ ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutable>
*/
static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem> &items,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
const ASSizeRange &sizeRange,
const CGSize size)
{
for (ASStackUnpositionedItem &item : items) {
const id<ASLayoutable> child = item.child;
@@ -227,7 +227,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem
0,
0,
crossDimension(style.direction, sizeRange.min),
crossDimension(style.direction, sizeRange.max));
crossDimension(style.direction, sizeRange.max),
size);
}
}
}
@@ -247,6 +248,7 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem
static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem> &items,
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange,
const CGSize size,
const BOOL useOptimizedFlexing)
{
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
@@ -258,7 +260,7 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
if (flexibleChildren == 0) {
// If optimized flexing was used then we have to clean up the unsized children, and lay them out at zero size
if (useOptimizedFlexing) {
layoutFlexibleChildrenAtZeroSize(items, style, sizeRange);
layoutFlexibleChildrenAtZeroSize(items, style, sizeRange, size);
}
return;
}
@@ -280,7 +282,8 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
MAX(flexedStackSize, 0),
MAX(flexedStackSize, 0),
crossDimension(style.direction, sizeRange.min),
crossDimension(style.direction, sizeRange.max));
crossDimension(style.direction, sizeRange.max),
size);
isFirstFlex = NO;
}
}
@@ -298,23 +301,19 @@ 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<ASLayoutable> child) -> ASStackUnpositionedItem {
const ASRelativeDimension flexBasis = child.flexBasis;
const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, flexBasis);
const CGFloat exactStackDimension = ASRelativeDimensionResolve(flexBasis, stackDimension(style.direction, size));
if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
return { child, [ASLayout layoutWithLayoutableObject:child constrainedSizeRange:sizeRange size:{0, 0}] };
return { child, [ASLayout layoutWithLayoutable:child size:{0, 0}] };
} else {
return {
child,
crossChildLayout(child,
style,
isUnconstrainedFlexBasis ? 0 : exactStackDimension,
isUnconstrainedFlexBasis ? INFINITY : exactStackDimension,
ASDimensionResolve(child.flexBasis, stackDimension(style.direction, size), 0),
ASDimensionResolve(child.flexBasis, stackDimension(style.direction, size), INFINITY),
minCrossDimension,
maxCrossDimension)
maxCrossDimension,
size)
};
}
});
@@ -324,9 +323,11 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange)
{
// If we have a fixed size in either dimension, pass it to children so they can resolve percentages against it.
// Otherwise, we pass ASLayoutableParentDimensionUndefined since it will depend on the content.
const CGSize size = {
sizeRange.max.width,
sizeRange.max.height
(sizeRange.min.width == sizeRange.max.width) ? sizeRange.min.width : ASLayoutableParentDimensionUndefined,
(sizeRange.min.height == sizeRange.max.height) ? sizeRange.min.height : ASLayoutableParentDimensionUndefined,
};
// We may be able to avoid some redundant layout passes
@@ -341,8 +342,8 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
size,
optimizedFlexing);
flexChildrenAlongStackDimension(items, style, sizeRange, optimizedFlexing);
stretchChildrenAlongCrossDimension(items, style);
flexChildrenAlongStackDimension(items, style, sizeRange, size, optimizedFlexing);
stretchChildrenAlongCrossDimension(items, style, size);
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
return {items, stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)};

View File

@@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
@abstract Creates the stack of TextKit components.
@param attributedSeedString The attributed string to seed the returned text storage with, or nil to receive an blank text storage.
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and FLT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.
*/
@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
@abstract Creates the stack of TextKit components.
@param textStorage The NSTextStorage to use.
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and FLT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
@param layoutManager The NSLayoutManager to use.
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.

View File

@@ -56,7 +56,7 @@
// If our text-view's width is already the constrained width, we can use our existing TextKit stack for this sizing calculation.
// Otherwise, we create a temporary stack to size for `constrainedWidth`.
if (CGRectGetWidth(components.textView.bounds) != constrainedWidth) {
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, FLT_MAX)];
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)];
}
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by -usedRectForTextContainer:).

View File

@@ -99,7 +99,7 @@
if (_sizingTextContainer == nil) {
// make this text container unbounded in height so that the layout manager will compute the total
// number of lines and not stop counting when height runs out.
_sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, FLT_MAX)];
_sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX)];
_sizingTextContainer.lineFragmentPadding = 0;
// use 0 regardless of what is in the attributes so that we get an accurate line count
@@ -156,7 +156,7 @@
NSRange longestWordRange = [str rangeOfString:longestWordNeedingResize];
NSMutableAttributedString *attrString = [textStorage attributedSubstringFromRange:longestWordRange].mutableCopy;
CGSize longestWordSize = [attrString boundingRectWithSize:CGSizeMake(FLT_MAX, FLT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
CGSize longestWordSize = [attrString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
// check if the longest word is larger than our constrained width
if (longestWordSize.width > _constrainedSize.width) {

View File

@@ -65,7 +65,7 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi
- (CGRect)finalFrameForNode:(ASDisplayNode *)node
{
for (ASLayout *layout in [self layoutForKey:ASTransitionContextToLayoutKey].sublayouts) {
if (layout.layoutableObject == node) {
if (layout.layoutable == node) {
return [layout frame];
}
}
@@ -76,7 +76,7 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi
{
NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray array];
for (ASLayout *sublayout in [self layoutForKey:key].sublayouts) {
[subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject];
[subnodes addObject:(ASDisplayNode *)sublayout.layoutable];
}
return subnodes;
}

View File

@@ -24,8 +24,7 @@ static const ASSizeRange kSize = {{320, 320}, {320, 320}};
- (void)testBackground
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor]);
foregroundNode.staticSize = {20, 20};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor], {20, 20});
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec

View File

@@ -45,10 +45,16 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {70, 100};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:[ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:options sizingOptions:sizingOptions child:foregroundNode]background:backgroundNode];
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:
[ASCenterLayoutSpec
centerLayoutSpecWithCenteringOptions:options
sizingOptions:sizingOptions
child:foregroundNode]
background:backgroundNode];
[self testLayoutSpec:layoutSpec
sizeRange:kSize
@@ -83,11 +89,23 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce
- (void)testMinimumSizeRangeIsGivenToChildWhenNotCentering
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
foregroundNode.staticSize = {10, 10};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10));
foregroundNode.flexGrow = YES;
ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:{} child:[ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[foregroundNode]] background:backgroundNode]];
ASCenterLayoutSpec *layoutSpec =
[ASCenterLayoutSpec
centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringNone
sizingOptions:{}
child:
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStart
children:@[foregroundNode]]
background:backgroundNode]];
[self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil];
}

View File

@@ -77,7 +77,7 @@
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack1, stack2, node5]];
};
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node5);
XCTAssertEqual(node.subnodes[1], node1);
XCTAssertEqual(node.subnodes[2], node2);
@@ -104,13 +104,13 @@
}
};
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node1);
XCTAssertEqual(node.subnodes[1], node2);
node.layoutState = @2;
[node invalidateCalculatedLayout];
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node1);
XCTAssertEqual(node.subnodes[1], node3);
@@ -157,12 +157,12 @@
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node1);
node.layoutState = @2;
[node invalidateCalculatedLayout];
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
// Dispatch back to the main thread to let the insertion / deletion of subnodes happening
dispatch_async(dispatch_get_main_queue(), ^{
@@ -202,7 +202,7 @@
XCTestExpectation *expectation = [self expectationWithDescription:@"Fix IHM layout transition also if one node is already loaded"];
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node1);
node.layoutState = @2;

View File

@@ -22,8 +22,9 @@
{
CGSize nodeSize = CGSizeMake(100, 100);
ASStaticSizeDisplayNode *displayNode = [ASStaticSizeDisplayNode new];
displayNode.staticSize = nodeSize;
ASDisplayNode *displayNode = [[ASDisplayNode alloc] init];
displayNode.width = ASDimensionMake(100);
displayNode.height = ASDimensionMake(100);
// Use a button node in here as ASButtonNode uses layoutSpecThatFits:
ASButtonNode *buttonNode = [ASButtonNode new];
@@ -47,8 +48,8 @@
{
CGSize nodeSize = CGSizeMake(100, 100);
ASStaticSizeDisplayNode *displayNode = [ASStaticSizeDisplayNode new];
displayNode.staticSize = nodeSize;
ASDisplayNode *displayNode = [ASDisplayNode new];
[displayNode setSizeWithCGSize:nodeSize];
ASButtonNode *buttonNode = [ASButtonNode new];
[displayNode addSubnode:buttonNode];
@@ -79,7 +80,7 @@
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:someOtherNode];
};
XCTAssertThrows([displayNode measure:CGSizeMake(100, 100)], @"Should throw if subnode was added in layoutSpecThatFits:");
XCTAssertThrows([displayNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))], @"Should throw if subnode was added in layoutSpecThatFits:");
}
- (void)testNotAllowModifyingSubnodesInLayoutSpecThatFits
@@ -95,7 +96,7 @@
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:someOtherNode];
};
XCTAssertThrows([displayNode measure:CGSizeMake(100, 100)], @"Should throw if subnodes where modified in layoutSpecThatFits:");
XCTAssertThrows([displayNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))], @"Should throw if subnodes where modified in layoutSpecThatFits:");
}
#endif
@@ -103,8 +104,8 @@
{
CGSize nodeSize = CGSizeMake(100, 100);
ASStaticSizeDisplayNode *displayNode = [ASStaticSizeDisplayNode new];
displayNode.staticSize = nodeSize;
ASDisplayNode *displayNode = [ASDisplayNode new];
[displayNode setSizeWithCGSize:nodeSize];
ASButtonNode *buttonNode = [ASButtonNode new];
[displayNode addSubnode:buttonNode];
@@ -121,7 +122,7 @@
[displayNode.view layoutIfNeeded];
XCTAssertEqual(numberOfLayoutSpecThatFitsCalls, 1, @"Should measure during layout if not measured");
[displayNode measureWithSizeRange:ASSizeRangeMake(nodeSize, nodeSize)];
[displayNode layoutThatFits:ASSizeRangeMake(nodeSize, nodeSize)];
XCTAssertEqual(numberOfLayoutSpecThatFitsCalls, 1, @"Should not remeasure with same bounds");
}

View File

@@ -28,7 +28,7 @@
node.layoutSpecBlock = ^(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5, 5, 5, 5) child:subnode];
};
[node measure:CGSizeMake(100, 100)];
[node layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
ASSnapshotVerifyNode(node, nil);
}

View File

@@ -1991,7 +1991,7 @@ static bool stringContainsPointer(NSString *description, id p) {
- (void)DISABLED_testThatItsSafeToAutomeasureANodeMidTransition
{
ASDisplayNode *supernode = [[ASDisplayNode alloc] init];
[supernode measure:CGSizeMake(100, 100)];
[supernode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.bounds = CGRectMake(0, 0, 50, 50);
[supernode addSubnode:node];

View File

@@ -11,6 +11,7 @@
//
#import <XCTest/XCTest.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASEditableTextNode.h>
static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
@@ -138,23 +139,11 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
XCTAssertTrue(secureEditableTextNode.textView.secureTextEntry == YES, @"textView's isSecureTextEntry should be YES.");
}
- (void)testSetPreferredFrameSize
{
CGSize preferredFrameSize = CGSizeMake(100, 100);
_editableTextNode.preferredFrameSize = preferredFrameSize;
CGSize calculatedSize = [_editableTextNode measure:CGSizeZero];
XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated width (%f) should be equal to preferred width (%f)", calculatedSize.width, preferredFrameSize.width);
XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated height (%f) should be equal to preferred height (%f)", calculatedSize.width, preferredFrameSize.width);
_editableTextNode.preferredFrameSize = CGSizeZero;
}
- (void)testCalculatedSizeIsGreaterThanOrEqualToConstrainedSize
{
for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_editableTextNode measure:constrainedSize];
CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertTrue(calculatedSize.width <= constrainedSize.width, @"Calculated width (%f) should be less than or equal to constrained width (%f)", calculatedSize.width, constrainedSize.width);
XCTAssertTrue(calculatedSize.height <= constrainedSize.height, @"Calculated height (%f) should be less than or equal to constrained height (%f)", calculatedSize.height, constrainedSize.height);
}
@@ -164,8 +153,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
{
for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_editableTextNode measure:constrainedSize];
CGSize recalculatedSize = [_editableTextNode measure:calculatedSize];
CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 4.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
}
@@ -175,8 +164,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
{
for (CGFloat i = 10; i < 500; i *= 1.3) {
CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_editableTextNode measure:constrainedSize];
CGSize recalculatedSize = [_editableTextNode measure:calculatedSize];
CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 11.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
}

View File

@@ -20,7 +20,8 @@
- (UIImage *)testImage
{
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"logo-square"
ofType:@"png" inDirectory:@"TestResources"];
ofType:@"png"
inDirectory:@"TestResources"];
return [UIImage imageWithContentsOfFile:path];
}
@@ -29,30 +30,32 @@
// trivial test case to ensure ASSnapshotTestCase works
ASImageNode *imageNode = [[ASImageNode alloc] init];
imageNode.image = [self testImage];
[imageNode measure:CGSizeMake(100, 100)];
[imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
ASSnapshotVerifyNode(imageNode, nil);
}
- (void)testForcedScaling
{
ASImageNode *imageNode = [[ASImageNode alloc] init];
CGSize forcedImageSize = CGSizeMake(100, 100);
ASImageNode *imageNode = [[ASImageNode alloc] init];
imageNode.forcedSize = forcedImageSize;
imageNode.image = [self testImage];
imageNode.forcedSize = CGSizeMake(100, 100);
// Snapshot testing requires that node is formally laid out.
imageNode.preferredFrameSize = CGSizeMake(100, 100);
[imageNode measure:CGSizeMake(100, 100)];
[imageNode setSizeWithCGSize:forcedImageSize];
[imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, forcedImageSize)];
ASSnapshotVerifyNode(imageNode, @"first");
imageNode.preferredFrameSize = CGSizeMake(200, 200);
[imageNode measure:CGSizeMake(200, 200)];
[imageNode setSizeWithCGSize:CGSizeMake(200, 200)];
[imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(200, 200))];
ASSnapshotVerifyNode(imageNode, @"second");
XCTAssert(CGImageGetWidth((CGImageRef)imageNode.contents) == 100 * imageNode.contentsScale && CGImageGetHeight((CGImageRef)imageNode.contents) == 100 * imageNode.contentsScale, @"contents should be 100 x 100 by contents scale.");
XCTAssert(CGImageGetWidth((CGImageRef)imageNode.contents) == forcedImageSize.width * imageNode.contentsScale &&
CGImageGetHeight((CGImageRef)imageNode.contents) == forcedImageSize.height * imageNode.contentsScale,
@"Contents should be 100 x 100 by contents scale.");
}
@end

View File

@@ -50,12 +50,14 @@ static NSString *nameForInsets(UIEdgeInsets insets)
for (NSUInteger combination = 0; combination < 16; combination++) {
UIEdgeInsets insets = insetsForCombination(combination, 10);
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {10, 10};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {10, 10});
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode]
backgroundLayoutSpecWithChild:
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:insets
child:foregroundNode]
background:backgroundNode];
static ASSizeRange kVariableSize = {{0, 0}, {300, 300}};
@@ -71,12 +73,14 @@ static NSString *nameForInsets(UIEdgeInsets insets)
for (NSUInteger combination = 0; combination < 16; combination++) {
UIEdgeInsets insets = insetsForCombination(combination, 10);
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {10, 10};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {10, 10});
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode]
backgroundLayoutSpecWithChild:
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:insets
child:foregroundNode]
background:backgroundNode];
static ASSizeRange kFixedSize = {{300, 300}, {300, 300}};
@@ -93,12 +97,14 @@ static NSString *nameForInsets(UIEdgeInsets insets)
for (NSUInteger combination = 0; combination < 16; combination++) {
UIEdgeInsets insets = insetsForCombination(combination, 0);
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {10, 10};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {10, 10});
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode]
backgroundLayoutSpecWithChild:
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:insets
child:foregroundNode]
background:backgroundNode];
static ASSizeRange kFixedSize = {{300, 300}, {300, 300}};

View File

@@ -31,17 +31,16 @@
identifier:(NSString *)identifier;
@end
@interface ASStaticSizeDisplayNode : ASDisplayNode
@property (nonatomic) CGSize staticSize;
@end
static inline ASStaticSizeDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor)
{
ASStaticSizeDisplayNode *node = [[ASStaticSizeDisplayNode alloc] init];
__attribute__((overloadable)) static inline ASDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor, CGSize size) {
ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.layerBacked = YES;
node.backgroundColor = backgroundColor;
node.staticSize = CGSizeZero;
node.width = ASDimensionMakeWithPoints(size.width);
node.height = ASDimensionMakeWithPoints(size.height);
return node;
}
__attribute__((overloadable)) static inline ASDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor)
{
return ASDisplayNodeWithBackgroundColor(backgroundColor, CGSizeZero);
}

View File

@@ -39,7 +39,7 @@
node.layoutSpecUnderTest = layoutSpec;
[node measureWithSizeRange:sizeRange];
[node layoutThatFits:sizeRange];
ASSnapshotVerifyNode(node, identifier);
}
@@ -60,12 +60,3 @@
}
@end
@implementation ASStaticSizeDisplayNode
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
{
return _staticSize;
}
@end

View File

@@ -23,8 +23,7 @@ static const ASSizeRange kSize = {{320, 320}, {320, 320}};
- (void)testOverlay
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor]);
foregroundNode.staticSize = {20, 20};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor], {20, 20});
ASLayoutSpec *layoutSpec =
[ASOverlayLayoutSpec

View File

@@ -21,8 +21,7 @@ static const ASSizeRange kFixedSize = {{0, 0}, {100, 100}};
- (void)testRatioLayoutSpecWithRatio:(CGFloat)ratio childSize:(CGSize)childSize identifier:(NSString *)identifier
{
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
subnode.staticSize = childSize;
ASDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], childSize);
ASLayoutSpec *layoutSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:subnode];

View File

@@ -23,7 +23,6 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
- (void)testWithOptions
{
[self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionStart];
[self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionCenter];
[self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionEnd];
@@ -57,14 +56,16 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
sizingOptions:(ASRelativeLayoutSpecSizingOption)sizingOptions
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
foregroundNode.staticSize = {70, 100};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:
[ASRelativeLayoutSpec
relativePositionLayoutSpecWithHorizontalPosition:horizontalPosition verticalPosition:verticalPosition sizingOption:sizingOptions child:foregroundNode]
relativePositionLayoutSpecWithHorizontalPosition:horizontalPosition
verticalPosition:verticalPosition
sizingOption:sizingOptions
child:foregroundNode]
background:backgroundNode];
[self testLayoutSpec:layoutSpec
@@ -105,17 +106,26 @@ static NSString *suffixForPositionOptions(ASRelativeLayoutSpecPosition horizonta
- (void)testMinimumSizeRangeIsGivenToChildWhenNotPositioning
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
foregroundNode.staticSize = {10, 10};
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10));
foregroundNode.flexGrow = YES;
ASLayoutSpec *childSpec = [ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[foregroundNode]]
background:backgroundNode];
ASLayoutSpec *childSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStart
children:@[foregroundNode]]
background:backgroundNode];
ASRelativeLayoutSpec *layoutSpec = [ASRelativeLayoutSpec
relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart verticalPosition:ASRelativeLayoutSpecPositionStart sizingOption:{} child:childSpec];
ASRelativeLayoutSpec *layoutSpec =
[ASRelativeLayoutSpec
relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart
verticalPosition:ASRelativeLayoutSpecPositionStart
sizingOption:{}
child:childSpec];
[self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil];
}

View File

@@ -23,26 +23,31 @@
#pragma mark - Utility methods
static NSArray *defaultSubnodes()
static NSArray<ASDisplayNode *> *defaultSubnodes()
{
return defaultSubnodesWithSameSize(CGSizeZero, NO);
}
static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
static NSArray<ASDisplayNode *> *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
NSArray *subnodes = @[
ASDisplayNodeWithBackgroundColor([UIColor redColor]),
ASDisplayNodeWithBackgroundColor([UIColor blueColor]),
ASDisplayNodeWithBackgroundColor([UIColor greenColor])
];
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.staticSize = subnodeSize;
NSArray<ASDisplayNode *> *subnodes = @[
ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize),
ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize),
ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize)
];
for (ASDisplayNode *subnode in subnodes) {
subnode.flexGrow = flex;
subnode.flexShrink = flex;
}
return subnodes;
}
static void setCGSizeToNode(CGSize size, ASDisplayNode *node)
{
node.width = ASDimensionMakeWithPoints(size.width);
node.height = ASDimensionMakeWithPoints(size.height);
}
- (void)testStackLayoutSpecWithJustify:(ASStackLayoutJustifyContent)justify
flex:(BOOL)flex
sizeRange:(ASSizeRange)sizeRange
@@ -53,7 +58,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = justify
};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, flex);
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, flex);
[self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier];
}
@@ -63,13 +68,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment
identifier:(NSString *)identifier
{
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init];
stackLayoutSpec.direction = direction;
stackLayoutSpec.children = subnodes;
[stackLayoutSpec setHorizontalAlignment:horizontalAlignment];
[stackLayoutSpec setVerticalAlignment:verticalAlignment];
stackLayoutSpec.horizontalAlignment = horizontalAlignment;
stackLayoutSpec.verticalAlignment = verticalAlignment;
CGSize exactSize = CGSizeMake(200, 200);
static ASSizeRange kSize = ASSizeRangeMake(exactSize, exactSize);
@@ -90,11 +95,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
subnodes:(NSArray *)subnodes
identifier:(NSString *)identifier
{
ASStackLayoutSpec *stackLayoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction
spacing:style.spacing
justifyContent:style.justifyContent
alignItems:style.alignItems
children:children];
ASStackLayoutSpec *stackLayoutSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:style.direction
spacing:style.spacing
justifyContent:style.justifyContent
alignItems:style.alignItems
children:children];
[self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
}
@@ -104,7 +112,6 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
identifier:(NSString *)identifier
{
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]);
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:stackLayoutSpec background:backgroundNode];
NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode];
@@ -145,8 +152,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
((ASDisplayNode *)subnodes[1]).flexShrink = YES;
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
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}};
@@ -157,8 +164,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, YES);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, YES);
setCGSizeToNode({150, 150}, subnodes[1]);
// width 300px; height 0-150px.
static ASSizeRange kUnderflowSize = {{300, 0}, {300, 150}};
@@ -173,10 +180,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 50};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 50}, subnodes[1]);
setCGSizeToNode({150, 50}, subnodes[2]);
// width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@@ -194,10 +201,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.spacing = 10
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 50};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 50}, subnodes[1]);
setCGSizeToNode({150, 50}, subnodes[2]);
// width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@@ -212,12 +219,16 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
ASLayoutSpec *layoutSpec =
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:{10, 10, 10 ,10}
insetLayoutSpecWithInsets:{10, 10, 10, 10}
child:
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:10 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[]]
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:10
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[]]
background:backgroundNode]];
// width 300px; height 0-300px
@@ -231,28 +242,28 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
static ASSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}};
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20;
subnodes[1].spacingBefore = 10;
subnodes[2].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;
subnodes[1].spacingBefore = 0;
subnodes[2].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20;
subnodes[1].spacingAfter = 10;
subnodes[2].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;
subnodes[1].spacingAfter = 0;
subnodes[2].spacingAfter = 0;
style.spacing = 10;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10;
subnodes[1].spacingBefore = -10;
subnodes[1].spacingAfter = -10;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"];
}
@@ -263,14 +274,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = ASStackLayoutJustifyContentCenter
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
subnodes[0].spacingBefore = 0;
subnodes[1].spacingBefore = 20;
subnodes[2].spacingBefore = 30;
// width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@@ -284,8 +295,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = ASStackLayoutJustifyContentSpaceBetween
};
ASStaticSizeDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
child.staticSize = {50, 50};
ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
// width 300px; height 0-INF
static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}};
@@ -299,8 +309,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = ASStackLayoutJustifyContentSpaceAround
};
ASStaticSizeDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
child.staticSize = {50, 50};
ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
// width 300px; height 0-INF
static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}};
@@ -325,12 +334,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
ASStaticSizeDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASStaticSizeDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
subnode2.staticSize = {50, 50};
ASDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1];
child1.flexBasis = ASRelativeDimensionMakeWithFraction(1);
child1.flexBasis = ASDimensionMakeWithFraction(1);
child1.flexGrow = YES;
child1.flexShrink = YES;
@@ -345,15 +353,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsCenter
};
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
subnode1.staticSize = {100, 100};
ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100});
subnode1.flexShrink = YES;
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
subnode2.staticSize = {50, 50};
ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50});
subnode2.flexShrink = YES;
NSArray *subnodes = @[subnode1, subnode2];
NSArray<ASDisplayNode *> *subnodes = @[subnode1, subnode2];
static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}};
[self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil];
}
@@ -362,14 +368,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
subnode1.staticSize = {100, 100};
ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100});
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
subnode2.staticSize = {50, 50};
ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50});
subnode2.alignSelf = ASStackLayoutAlignSelfCenter;
NSArray *subnodes = @[subnode1, subnode2];
NSArray<ASDisplayNode *> *subnodes = @[subnode1, subnode2];
static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
[self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil];
}
@@ -382,14 +386,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsStart
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
subnodes[0].spacingBefore = 0;
subnodes[1].spacingBefore = 20;
subnodes[2].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@@ -403,14 +407,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsEnd
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
subnodes[0].spacingBefore = 0;
subnodes[1].spacingBefore = 20;
subnodes[2].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@@ -424,14 +428,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsCenter
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
subnodes[0].spacingBefore = 0;
subnodes[1].spacingBefore = 20;
subnodes[2].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@@ -445,14 +449,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsStretch
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
subnodes[0].spacingBefore = 0;
subnodes[1].spacingBefore = 20;
subnodes[2].spacingBefore = 30;
static ASSizeRange kVariableSize = {{200, 200}, {300, 300}};
// all children should be 200px wide
@@ -467,14 +471,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsStretch
};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({100, 70}, subnodes[1]);
setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
subnodes[0].spacingBefore = 0;
subnodes[1].spacingBefore = 20;
subnodes[2].spacingBefore = 30;
static ASSizeRange kVariableSize = {{50, 50}, {300, 300}};
// all children should be 150px wide
@@ -491,12 +495,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
setCGSizeToNode({150, 150}, subnodes[1]);
for (ASStaticSizeDisplayNode *subnode in subnodes) {
for (ASDisplayNode *subnode in subnodes) {
subnode.flexGrow = YES;
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10);
subnode.flexBasis = ASDimensionMakeWithPoints(10);
}
// width 300px; height 0-150px.
@@ -512,15 +516,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
for (ASStaticSizeDisplayNode *subnode in subnodes) {
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
for (ASDisplayNode *subnode in subnodes) {
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 = ASRelativeDimensionMakeWithFraction(0.5);
subnodes[0].flexBasis = ASDimensionMakeWithFraction(0.5);
static ASSizeRange kSize = {{200, 0}, {200, INFINITY}};
[self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil];
@@ -530,13 +533,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
setCGSizeToNode({50, 50}, subnodes[0]);
setCGSizeToNode({150, 150}, subnodes[1]);
setCGSizeToNode({150, 50}, subnodes[2]);
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20);
for (ASDisplayNode *subnode in subnodes) {
subnode.flexBasis = ASDimensionMakeWithPoints(20);
}
static ASSizeRange kSize = {{300, 0}, {300, 150}};
@@ -545,13 +548,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
- (void)testCrossAxisStretchingOccursAfterStackAxisFlexing
{
NSArray *subnodes = @[
ASDisplayNodeWithBackgroundColor([UIColor greenColor]),
ASDisplayNodeWithBackgroundColor([UIColor blueColor]),
ASDisplayNodeWithBackgroundColor([UIColor redColor])
];
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {10, 0};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000};
NSArray<ASDisplayNode *> *subnodes = @[
ASDisplayNodeWithBackgroundColor([UIColor greenColor]),
ASDisplayNodeWithBackgroundColor([UIColor blueColor], {10, 0}),
ASDisplayNodeWithBackgroundColor([UIColor redColor], {3000, 3000})
];
ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]];
child2.flexGrow = YES;
@@ -565,7 +566,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10)
child:
[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[subnodes[1], child2,]]
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[subnodes[1], child2]]
]
background:subnodes[0]];
@@ -576,17 +582,16 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
- (void)testViolationIsDistributedEquallyAmongFlexibleChildren
{
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
NSArray *subnodes = defaultSubnodes();
setCGSizeToNode({300, 50}, subnodes[0]);
setCGSizeToNode({100, 50}, subnodes[1]);
setCGSizeToNode({200, 50}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50};
((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES;
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO;
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50};
((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES;
subnodes[0].flexShrink = YES;
subnodes[1].flexShrink = NO;
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

@@ -22,46 +22,34 @@
- (void)testSizingBehaviour
{
[self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(FLT_MAX, FLT_MAX))
[self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(INFINITY, INFINITY))
identifier:@"underflowChildren"];
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(50, 100))
identifier:@"overflowChildren"];
// Expect the spec to wrap its content because children sizes are between constrained size
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX / 2, FLT_MAX / 2))
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY / 2, INFINITY / 2))
identifier:@"wrappedChildren"];
}
- (void)testChildrenMeasuredWithAutoMaxSize
{
ASStaticSizeDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor], (CGSize){50, 50});
firstChild.layoutPosition = CGPointMake(0, 0);
firstChild.staticSize = CGSizeMake(50, 50);
ASStaticSizeDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor], (CGSize){100, 100});
secondChild.layoutPosition = CGPointMake(10, 60);
secondChild.staticSize = CGSizeMake(100, 100);
ASSizeRange sizeRange = ASSizeRangeMake(CGSizeMake(10, 10), CGSizeMake(110, 160));
[self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:nil];
XCTAssertTrue(ASSizeRangeEqualToSizeRange(firstChild.constrainedSizeForCalculatedLayout,
ASSizeRangeMake(CGSizeZero, sizeRange.max)));
CGSize secondChildMaxSize = CGSizeMake(sizeRange.max.width - secondChild.layoutPosition.x,
sizeRange.max.height - secondChild.layoutPosition.y);
XCTAssertTrue(ASSizeRangeEqualToSizeRange(secondChild.constrainedSizeForCalculatedLayout,
ASSizeRangeMake(CGSizeZero, secondChildMaxSize)));
}
- (void)testWithSizeRange:(ASSizeRange)sizeRange identifier:(NSString *)identifier
{
ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor], (CGSize){50, 50});
firstChild.layoutPosition = CGPointMake(0, 0);
firstChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor], (CGSize){100, 100});
secondChild.layoutPosition = CGPointMake(0, 50);
secondChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(100, 100));
[self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:identifier];
}
@@ -73,9 +61,12 @@
NSMutableArray *subnodes = [NSMutableArray arrayWithArray:children];
[subnodes insertObject:backgroundNode atIndex:0];
ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:staticLayoutSpec
background:backgroundNode];
ASLayoutSpec *layoutSpec =
[ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild:
[ASStaticLayoutSpec
staticLayoutSpecWithChildren:children]
background:backgroundNode];
[self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
}

View File

@@ -148,7 +148,7 @@
- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath
{
return ASSizeRangeMakeExactSize(CGSizeMake(10, 42));
return ASSizeRangeMake(CGSizeMake(10, 42));
}
@end

View File

@@ -9,6 +9,7 @@
#import <XCTest/XCTest.h>
#import "ASPerformanceTestContext.h"
#import <AsyncDisplayKit/ASTextNode.h>
#import <AsyncDisplayKit/ASLayout.h>
#import "ASinternalHelpers.h"
#import "ASXCTExtensions.h"
#include "CGRect+ASConvenience.h"
@@ -71,7 +72,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
NSAttributedString *text = data[i % data.count];
startMeasuring();
node.attributedText = text;
asdkSize = [node measure:maxSize];
asdkSize = [node layoutThatFits:ASSizeRangeMake(CGSizeZero, maxSize)].size;
stopMeasuring();
}];
ctx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);
@@ -101,7 +102,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
ASTextNode *node = [[ASTextNode alloc] init];
startMeasuring();
node.attributedText = text;
asdkSize = [node measure:maxSize];
asdkSize = [node layoutThatFits:ASSizeRangeMake(CGSizeZero, maxSize)].size;
stopMeasuring();
}];
ctx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);
@@ -131,7 +132,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
ASTextNode *node = [[ASTextNode alloc] init];
startMeasuring();
node.attributedText = text;
asdkSize = [node measure:maxSize];
asdkSize = [node layoutThatFits:ASSizeRangeMake(CGSizeZero, maxSize)].size;
stopMeasuring();
}];
testCtx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);

View File

@@ -10,8 +10,8 @@
//
#import "ASSnapshotTestCase.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "ASLayout.h"
@interface ASTextNodeSnapshotTests : ASSnapshotTestCase
@@ -25,7 +25,7 @@
ASTextNode *textNode = [[ASTextNode alloc] init];
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"judar"
attributes:@{NSFontAttributeName : [UIFont italicSystemFontOfSize:24]}];
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
[textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
textNode.textContainerInset = UIEdgeInsetsMake(0, 2, 0, 2);
ASSnapshotVerifyNode(textNode, nil);
@@ -40,7 +40,7 @@
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"judar judar judar judar judar judar"
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:30] }];
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 80))];
[textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 80))];
textNode.frame = CGRectMake(50, 50, textNode.calculatedSize.width, textNode.calculatedSize.height);
textNode.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10);
@@ -62,7 +62,7 @@
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"yolo"
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:30] }];
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
[textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
textNode.frame = CGRectMake(50, 50, textNode.calculatedSize.width, textNode.calculatedSize.height);
textNode.textContainerInset = UIEdgeInsetsMake(5, 10, 10, 5);

View File

@@ -12,6 +12,7 @@
#import <OCMock/OCMock.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASTextNode.h>
#import <XCTest/XCTest.h>
@@ -111,7 +112,7 @@
{
for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_textNode measure:constrainedSize];
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertTrue(calculatedSize.width <= constrainedSize.width, @"Calculated width (%f) should be less than or equal to constrained width (%f)", calculatedSize.width, constrainedSize.width);
XCTAssertTrue(calculatedSize.height <= constrainedSize.height, @"Calculated height (%f) should be less than or equal to constrained height (%f)", calculatedSize.height, constrainedSize.height);
}
@@ -121,8 +122,8 @@
{
for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_textNode measure:constrainedSize];
CGSize recalculatedSize = [_textNode measure:constrainedSize];
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 4.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
}
@@ -132,8 +133,8 @@
{
for (CGFloat i = 10; i < 500; i *= 1.3) {
CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_textNode measure:constrainedSize];
CGSize recalculatedSize = [_textNode measure:constrainedSize];
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 11.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
}
@@ -143,9 +144,9 @@
{
_textNode.placeholderEnabled = YES;
XCTAssertNoThrow([_textNode measure:CGSizeZero], @"Measure with zero size and placeholder enabled should not throw an exception");
XCTAssertNoThrow([_textNode measure:CGSizeMake(0, 100)], @"Measure with zero width and placeholder enabled should not throw an exception");
XCTAssertNoThrow([_textNode measure:CGSizeMake(100, 0)], @"Measure with zero height and placeholder enabled should not throw an exception");
XCTAssertNoThrow([_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeZero)], @"Measure with zero size and placeholder enabled should not throw an exception");
XCTAssertNoThrow([_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(0, 100))], @"Measure with zero width and placeholder enabled should not throw an exception");
XCTAssertNoThrow([_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 0))], @"Measure with zero height and placeholder enabled should not throw an exception");
}
- (void)testAccessibility
@@ -170,7 +171,7 @@
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
_textNode.delegate = delegate;
[_textNode measure:CGSizeMake(100, 100)];
[_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
NSRange returnedLinkRange;
NSString *returnedAttributeName;
NSString *returnedLinkAttributeValue = [_textNode linkAttributeValueAtPoint:CGPointMake(3, 3) attributeName:&returnedAttributeName range:&returnedLinkRange];
@@ -192,7 +193,7 @@
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
_textNode.delegate = delegate;
CGSize calculatedSize = [_textNode measure:CGSizeMake(100, 100)];
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))].size;
NSRange returnedLinkRange = NSMakeRange(NSNotFound, 0);
NSRange expectedRange = NSMakeRange(NSNotFound, 0);
NSString *returnedAttributeName;
@@ -218,9 +219,9 @@
- (void)testAddingExclusionPathsShouldInvalidateAndIncreaseTheSize
{
CGSize constrainedSize = CGSizeMake(100, CGFLOAT_MAX);
CGSize sizeWithoutExclusionPaths = [_textNode measure:constrainedSize];
CGSize sizeWithoutExclusionPaths = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
_textNode.exclusionPaths = @[[UIBezierPath bezierPathWithRect:CGRectMake(50, 20, 30, 40)]];
CGSize sizeWithExclusionPaths = [_textNode measure:constrainedSize];
CGSize sizeWithExclusionPaths = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
XCTAssertGreaterThan(sizeWithExclusionPaths.height, sizeWithoutExclusionPaths.height, @"Setting exclusions paths should invalidate the calculated size and return a greater size");
}

View File

@@ -151,3 +151,5 @@
#define AS_UNAVAILABLE(message)
#endif
#endif
#define ASOVERLOADABLE __attribute__((overloadable))

View File

@@ -192,7 +192,7 @@
{
[super viewDidLayoutSubviews];
CGSize size = [self.transitionNode measure:self.view.frame.size];
CGSize size = [self.transitionNode layoutThatFits:ASSizeRangeMake(CGSizeZero, self.view.frame.size)].size;
self.transitionNode.frame = CGRectMake(0, 20, size.width, size.height);
}

View File

@@ -65,7 +65,8 @@
[self addSubnode:_likeButtonNode];
_muteButtonNode = [[ASButtonNode alloc] init];
_muteButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
_muteButtonNode.width = ASDimensionMakeWithPoints(16.0);
_muteButtonNode.height = ASDimensionMakeWithPoints(22.0);
[_muteButtonNode addTarget:self action:@selector(didTapMuteButton) forControlEvents:ASControlNodeEventTouchUpInside];
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url loadAssetWhenNodeBecomesVisible:YES];
@@ -86,12 +87,18 @@
};
}
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;
_videoPlayerNode.preferredFrameSize = CGSizeMake(fullWidth, fullWidth * 9 / 16);
_avatarNode.preferredFrameSize = CGSizeMake(AVATAR_IMAGE_HEIGHT, AVATAR_IMAGE_HEIGHT);
_likeButtonNode.preferredFrameSize = CGSizeMake(50.0, 26.0);
_videoPlayerNode.width = ASDimensionMakeWithPoints(fullWidth);
_videoPlayerNode.height = ASDimensionMakeWithPoints(fullWidth * 9 / 16);
_avatarNode.width = ASDimensionMakeWithPoints(AVATAR_IMAGE_HEIGHT);
_avatarNode.height = ASDimensionMakeWithPoints(AVATAR_IMAGE_HEIGHT);
_likeButtonNode.width = ASDimensionMakeWithPoints(50.0);
_likeButtonNode.height = ASDimensionMakeWithPoints(26.0);
ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
headerStack.spacing = HORIZONTAL_BUFFER;
@@ -104,15 +111,15 @@
ASStackLayoutSpec *bottomControlsStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
bottomControlsStack.spacing = HORIZONTAL_BUFFER;
bottomControlsStack.alignItems = ASStackLayoutAlignItemsCenter;
[bottomControlsStack setChildren:@[ _likeButtonNode]];
bottomControlsStack.children = @[_likeButtonNode];
UIEdgeInsets bottomControlsInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
UIEdgeInsets bottomControlsInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
ASInsetLayoutSpec *bottomControlsInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:bottomControlsInsets child:bottomControlsStack];
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
[verticalStack setChildren:@[ headerInset, _videoPlayerNode, bottomControlsInset ]];
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
verticalStack.children = @[headerInset, _videoPlayerNode, bottomControlsInset];
return verticalStack;
}
@@ -183,7 +190,10 @@
if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
ASDisplayNode *scrubber = controls[ @(ASVideoPlayerNodeControlTypeScrubber) ];
scrubber.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
scrubber.height = ASDimensionMakeWithPoints(44.0);
scrubber.minWidth = ASDimensionMakeWithPoints(0.0);
scrubber.maxWidth = ASDimensionMakeWithPoints(maxSize.width);
scrubber.flexGrow = YES;
}
NSArray *controlBarControls = [self controlsForControlBar:controls];

View File

@@ -126,7 +126,9 @@
// header stack
_userAvatarImageView.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); // constrain avatar image frame size
// constrain avatar image frame size
_userAvatarImageView.width = ASDimensionMakeWithPoints(USER_IMAGE_HEIGHT);
_userAvatarImageView.height = ASDimensionMakeWithPoints(USER_IMAGE_HEIGHT);
_photoTimeIntervalSincePostLabel.spacingBefore = HORIZONTAL_BUFFER; // to remove double spaces around spacer
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size?
@@ -155,7 +157,10 @@
// vertical stack
CGFloat cellWidth = constrainedSize.max.width;
_photoImageView.preferredFrameSize = CGSizeMake(cellWidth, cellWidth); // constrain photo frame size
// constrain photo frame size
_photoImageView.width = ASDimensionMakeWithPoints(cellWidth);
_photoImageView.height = ASDimensionMakeWithPoints(cellWidth);
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space

View File

@@ -115,66 +115,86 @@
{
#define SPACING 5
#define HEIGHT 30
CGSize preferredSize = CGSizeMake(constrainedSize.max.width * 0.3, HEIGHT);
CGSize nodeSize = CGSizeMake(constrainedSize.max.width * 0.3, HEIGHT);
_latEditableNode.preferredFrameSize = _lonEditableNode.preferredFrameSize = preferredSize;
_deltaLatEditableNode.preferredFrameSize = _deltaLonEditableNode.preferredFrameSize = preferredSize;
_updateRegionButton.preferredFrameSize = _liveMapToggleButton.preferredFrameSize = preferredSize;
[_latEditableNode setSizeWithCGSize:nodeSize];
[_lonEditableNode setSizeWithCGSize:nodeSize];
[_deltaLatEditableNode setSizeWithCGSize:nodeSize];
[_deltaLonEditableNode setSizeWithCGSize:nodeSize];
[_updateRegionButton setSizeWithCGSize:nodeSize];
[_liveMapToggleButton setSizeWithCGSize:nodeSize];
_latEditableNode.flexGrow = _lonEditableNode.flexGrow = true;
_deltaLatEditableNode.flexGrow = _deltaLonEditableNode.flexGrow = true;
_updateRegionButton.flexGrow = _liveMapToggleButton.flexGrow = true;
_latEditableNode.flexGrow = _lonEditableNode.flexGrow = YES;
_deltaLatEditableNode.flexGrow = _deltaLonEditableNode.flexGrow = YES;
_updateRegionButton.flexGrow = _liveMapToggleButton.flexGrow = YES;
_mapNode.flexGrow = true;
_mapNode.flexGrow = YES;
ASStackLayoutSpec * lonlatSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_latEditableNode, _lonEditableNode]];
ASStackLayoutSpec *lonlatSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_latEditableNode, _lonEditableNode]];
lonlatSpec.flexGrow = true;
ASStackLayoutSpec * deltaLonlatSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_deltaLatEditableNode, _deltaLonEditableNode]];
ASStackLayoutSpec *deltaLonlatSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_deltaLatEditableNode, _deltaLonEditableNode]];
deltaLonlatSpec.flexGrow = true;
ASStackLayoutSpec * lonlatConfigSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[lonlatSpec, deltaLonlatSpec]];
ASStackLayoutSpec *lonlatConfigSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[lonlatSpec, deltaLonlatSpec]];
lonlatConfigSpec.flexGrow = true;
ASStackLayoutSpec * buttonsSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[_updateRegionButton, _liveMapToggleButton]];
ASStackLayoutSpec *buttonsSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[_updateRegionButton, _liveMapToggleButton]];
buttonsSpec.flexGrow = true;
ASStackLayoutSpec * dashboardSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[lonlatConfigSpec, buttonsSpec]];
ASStackLayoutSpec *dashboardSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[lonlatConfigSpec, buttonsSpec]];
dashboardSpec.flexGrow = true;
ASInsetLayoutSpec * insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 10, 0, 10) child:dashboardSpec];
ASInsetLayoutSpec *insetSpec =
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 10, 0, 10)
child:dashboardSpec];
ASStackLayoutSpec * layoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[insetSpec, _mapNode ]];
ASStackLayoutSpec *layoutSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[insetSpec, _mapNode ]];
return layoutSpec;
}
#pragma mark - Button actions
-(void)updateRegion
- (void)updateRegion
{
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
f.numberStyle = NSNumberFormatterDecimalStyle;
@@ -190,7 +210,7 @@
_mapNode.region = region;
}
-(void)toggleLiveMap
- (void)toggleLiveMap
{
_mapNode.liveMap = !_mapNode.liveMap;
NSString * const liveMapStr = [self liveMapStr];

View File

@@ -39,7 +39,7 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
self.imageNode.position = CGPointZero;
self.imageNode.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
[self.imageNode setSizeWithCGSize:constrainedSize.max];
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.imageNode]];
}

View File

@@ -63,7 +63,7 @@ static const NSInteger kImageHeight = 200;
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
self.collectionNode.position = CGPointZero;
self.collectionNode.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
[self.collectionNode setSizeWithCGSize:constrainedSize.max];
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.collectionNode]];
}

View File

@@ -45,8 +45,10 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
self.node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.node]];
// 100% of container
_node.width = ASDimensionMakeWithFraction(1.0);
_node.height = ASDimensionMakeWithFraction(1.0);
return [ASWrapperLayoutSpec wrapperWithLayoutable:_node];
}
#pragma mark - <ASCollectionDataSource, ASCollectionDelegate>

View File

@@ -37,9 +37,7 @@ static UIColor *OverViewASPagerNodeRandomColor() {
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:constrainedSize.max];
return [ASLayout layoutWithLayoutable:self size:constrainedSize.max];
}
@end
@@ -69,8 +67,10 @@ static UIColor *OverViewASPagerNodeRandomColor() {
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_node]];
// 100% of container
_node.width = ASDimensionMakeWithFraction(1.0);
_node.height = ASDimensionMakeWithFraction(1.0);
return [ASWrapperLayoutSpec wrapperWithLayoutable:_node];
}
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode

View File

@@ -42,8 +42,10 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_node]];
// 100% of container
_node.width = ASDimensionMakeWithFraction(1.0);
_node.height = ASDimensionMakeWithFraction(1.0);
return [ASWrapperLayoutSpec wrapperWithLayoutable:_node];
}

View File

@@ -82,6 +82,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
BOOL hasDescription = self.descriptionNode.attributedText.length > 0;
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackLayoutSpec.alignItems = ASStackLayoutAlignItemsStart;
verticalStackLayoutSpec.spacing = 5.0;
verticalStackLayoutSpec.children = hasDescription ? @[self.titleNode, self.descriptionNode] : @[self.titleNode];
@@ -203,8 +204,11 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASImageNode
ASImageNode *imageNode = [ASImageNode new];
imageNode.image = [UIImage imageNamed:@"image"];
imageNode.preferredFrameSize = CGSizeMake(imageNode.image.size.width / 7, imageNode.image.size.height / 7);
imageNode.image = [UIImage imageNamed:@"image.jpg"];
CGSize imageNetworkImageNodeSize = (CGSize){imageNode.image.size.width / 7, imageNode.image.size.height / 7};
[imageNode setSizeWithCGSize:imageNetworkImageNodeSize];
parentNode = [self centeringParentNodeWithChild:imageNode];
parentNode.entryTitle = @"ASImageNode";
@@ -214,7 +218,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASNetworkImageNode
ASNetworkImageNode *networkImageNode = [ASNetworkImageNode new];
networkImageNode.URL = [NSURL URLWithString:@"http://i.imgur.com/FjOR9kX.jpg"];
networkImageNode.preferredFrameSize = CGSizeMake(imageNode.image.size.width / 7, imageNode.image.size.height / 7);
[networkImageNode setSizeWithCGSize:imageNetworkImageNodeSize];
parentNode = [self centeringParentNodeWithChild:networkImageNode];
parentNode.entryTitle = @"ASNetworkImageNode";
@@ -223,7 +227,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASMapNode
ASMapNode *mapNode = [ASMapNode new];
mapNode.preferredFrameSize = CGSizeMake(300.0, 300.0);
[mapNode setSizeWithCGSize:(CGSize){300.0, 300.0}];
// San Francisco
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194);
@@ -236,7 +240,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASVideoNode
ASVideoNode *videoNode = [ASVideoNode new];
videoNode.preferredFrameSize = CGSizeMake(300.0, 400.0);
[videoNode setSizeWithCGSize:(CGSize){300.0, 400.0}];
AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://www.w3schools.com/html/mov_bbb.mp4"]];
videoNode.asset = asset;
@@ -250,7 +254,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
UIImage *scrollNodeImage = [UIImage imageNamed:@"image"];
ASScrollNode *scrollNode = [ASScrollNode new];
scrollNode.preferredFrameSize = CGSizeMake(300.0, 400.0);
[scrollNode setSizeWithCGSize:(CGSize){300.0, 400.0}];
UIScrollView *scrollNodeView = scrollNode.view;
[scrollNodeView addSubview:[[UIImageView alloc] initWithImage:scrollNodeImage]];
@@ -391,6 +395,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
parentNode.entryDescription = @"Is based on a simplified version of CSS flexbox. It allows you to stack components vertically or horizontally and specify how they should be flexed and aligned to fit in the available space.";
parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) {
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackLayoutSpec.alignItems = ASStackLayoutAlignItemsStart;
verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3];
return verticalStackLayoutSpec;
};
@@ -401,17 +406,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark Horizontal ASStackLayoutSpec
childNode1 = [ASDisplayNode new];
childNode1.preferredFrameSize = CGSizeMake(10.0, 20);
[childNode1 setSizeWithCGSize:(CGSize){10.0, 20.0}];
childNode1.flexGrow = YES;
childNode1.backgroundColor = [UIColor greenColor];
childNode2 = [ASDisplayNode new];
childNode2.preferredFrameSize = CGSizeMake(10.0, 20.0);
[childNode2 setSizeWithCGSize:(CGSize){10.0, 20.0}];
childNode2.alignSelf = ASStackLayoutAlignSelfStretch;
childNode2.backgroundColor = [UIColor blueColor];
childNode3 = [ASDisplayNode new];
childNode3.preferredFrameSize = CGSizeMake(10.0, 20.0);
[childNode3 setSizeWithCGSize:(CGSize){10.0, 20.0}];
childNode3.backgroundColor = [UIColor yellowColor];
parentNode = [self parentNodeWithChild:childNode];
@@ -420,20 +425,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) {
// Create stack alyout spec to layout children
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3];
verticalStackLayoutSpec.spacing = 5.0; // Spacing between children
ASStackLayoutSpec *horizontalStackSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
horizontalStackSpec.alignItems = ASStackLayoutAlignItemsStart;
horizontalStackSpec.children = @[childNode1, childNode2, childNode3];
horizontalStackSpec.spacing = 5.0; // Spacing between children
// Layout the stack layout with 100% width and 100% height of the parent node
ASRelativeSizeRange sizeRange = ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimensionMakeWithFraction(1),
ASRelativeDimensionMakeWithFraction(1));
verticalStackLayoutSpec.sizeRange = sizeRange;
// Wrap the static stack layout in a static spec so it will grow to the whole parent node size
ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[verticalStackLayoutSpec]];
horizontalStackSpec.height = ASDimensionMakeWithFraction(1.0);
horizontalStackSpec.width = ASDimensionMakeWithFraction(1.0);
// Add a bit of inset
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0.0, 5.0, 0.0, 5.0) child:staticLayoutSpec];
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0.0, 5.0, 0.0, 5.0) child:horizontalStackSpec];
};
[parentNode addSubnode:childNode1];
[parentNode addSubnode:childNode2];
@@ -465,7 +467,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
- (OverviewDisplayNodeWithSizeBlock *)parentNodeWithChild:(ASDisplayNode *)child
{
OverviewDisplayNodeWithSizeBlock *parentNode = [OverviewDisplayNodeWithSizeBlock new];
parentNode.preferredFrameSize = CGSizeMake(100, 100);
[parentNode setSizeWithCGSize:(CGSize){100, 100}];
parentNode.backgroundColor = [UIColor redColor];
return parentNode;
}
@@ -489,7 +491,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
- (ASDisplayNode *)childNode
{
ASDisplayNode *childNode = [ASDisplayNode new];
childNode.preferredFrameSize = CGSizeMake(50, 50);
[childNode setSizeWithCGSize:(CGSize){50, 50}];
childNode.backgroundColor = [UIColor blueColor];
return childNode;
}

View File

@@ -51,12 +51,11 @@
// Center node frame
CGRect bounds = self.view.bounds;
CGSize nodeSize = self.node.preferredFrameSize;
if (CGSizeEqualToSize(nodeSize, CGSizeZero)) {
nodeSize = self.view.bounds.size;
}
self.node.frame = CGRectMake(CGRectGetMidX(bounds) - (nodeSize.width / 2.0), CGRectGetMidY(bounds) - (nodeSize.height / 2.0), nodeSize.width, nodeSize.height);
[self.node measure:self.node.bounds.size];
CGSize nodeSize = [self.node layoutThatFits:ASSizeRangeMake(CGSizeZero, bounds.size)].size;
self.node.frame = CGRectMake(CGRectGetMidX(bounds) - (nodeSize.width / 2.0),
CGRectGetMidY(bounds) - (nodeSize.height / 2.0),
nodeSize.width,
nodeSize.height);
}
@end

View File

@@ -107,7 +107,8 @@ const CGFloat kSoldOutGBHeight = 50.0;
self.soldOutLabelFlat.layerBacked = YES;
self.soldOutLabelBackground = [[ASDisplayNode alloc] init];
self.soldOutLabelBackground.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(1), ASRelativeDimensionMakeWithPoints(kSoldOutGBHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(1), ASRelativeDimensionMakeWithPoints(kSoldOutGBHeight)));
self.soldOutLabelBackground.width = ASDimensionMakeWithFraction(1.0);
self.soldOutLabelBackground.height = ASDimensionMakeWithPoints(kSoldOutGBHeight);
self.soldOutLabelBackground.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9];
self.soldOutLabelBackground.flexGrow = YES;
self.soldOutLabelBackground.layerBacked = YES;
@@ -289,7 +290,7 @@ const CGFloat kSoldOutGBHeight = 50.0;
ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:self.dealImageView];
self.badge.layoutPosition = CGPointMake(0, constrainedSize.max.height - kFixedLabelsAreaHeight - kBadgeHeight);
self.badge.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(0), ASRelativeDimensionMakeWithPoints(kBadgeHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(1), ASRelativeDimensionMakeWithPoints(kBadgeHeight)));
self.badge.height = ASDimensionMakeWithPoints(kBadgeHeight);
ASStaticLayoutSpec *badgePosition = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.badge]];
ASOverlayLayoutSpec *badgeOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imagePlace overlay:badgePosition];
@@ -300,8 +301,7 @@ const CGFloat kSoldOutGBHeight = 50.0;
- (ASLayoutSpec *)soldOutLabelSpec {
ASCenterLayoutSpec *centerSoldOutLabel = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionMinimumXY child:self.soldOutLabelFlat];
ASStaticLayoutSpec *soldOutBG = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.soldOutLabelBackground]];
ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:soldOutBG];
ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:self.soldOutLabelBackground];
ASBackgroundLayoutSpec *soldOutLabelOverBackground = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:centerSoldOutLabel background:centerSoldOut];
return soldOutLabelOverBackground;
}

View File

@@ -53,9 +53,8 @@ static CGFloat kFixedHeight = 200.0f;
[spinner startAnimating];
return spinner;
}];
_loadingSpinner.preferredFrameSize = CGSizeMake(50, 50);
[_loadingSpinner setSizeWithCGSize:CGSizeMake(50, 50)];
// add it as a subnode, and we're done
[self addSubnode:_loadingSpinner];

View File

@@ -34,17 +34,9 @@
return self;
}
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
[_imageNode measure:constrainedSize];
return constrainedSize;
}
- (void)layout
{
[super layout];
_imageNode.frame = CGRectMake(0, 0, _imageNode.calculatedSize.width, _imageNode.calculatedSize.height);
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_imageNode];
}
@end

View File

@@ -64,6 +64,7 @@ static const CGFloat kInnerPadding = 10.0f;
- (void)didLoad
{
[super didLoad];
_collectionNode.view.asyncDelegate = self;
_collectionNode.view.asyncDataSource = self;
}
@@ -75,21 +76,22 @@ static const CGFloat kInnerPadding = 10.0f;
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
{
CGSize elementSize = _elementSize;
return ^{
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
elementNode.preferredFrameSize = _elementSize;
[elementNode setSizeWithCGSize:elementSize];
return elementNode;
};
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_collectionNode.preferredFrameSize = CGSizeMake(self.bounds.size.width, _elementSize.height);
CGSize collectionNodeSize = CGSizeMake(constrainedSize.max.width, _elementSize.height);
[_collectionNode setSizeWithCGSize:collectionNodeSize];
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, 0.0, kOuterPadding, 0.0);
insetSpec.child = _collectionNode;
return insetSpec;
}

View File

@@ -68,13 +68,11 @@
_tableView.frame = self.view.bounds;
}
#pragma mark -
#pragma mark ASTableView.
#pragma mark - ASTableView.
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
HorizontalScrollCellNode *node = [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)];
return node;
return [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

View File

@@ -98,8 +98,7 @@ static const CGFloat kInnerPadding = 10.0f;
// lorem ipsum text, plus some nice styling
_textNode = [[ASTextNode alloc] init];
_textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum]
attributes:[self textStyle]];
_textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum] attributes:[self textStyle]];
[self addSubnode:_textNode];
// hairline cell separator
@@ -134,27 +133,36 @@ static const CGFloat kInnerPadding = 10.0f;
style.paragraphSpacing = 0.5 * font.lineHeight;
style.hyphenationFactor = 1.0;
return @{ NSFontAttributeName: font,
NSParagraphStyleAttributeName: style,
ASTextNodeWordKerningAttributeName : @.5};
return @{
NSFontAttributeName: font,
NSParagraphStyleAttributeName: style,
ASTextNodeWordKerningAttributeName : @.5
};
}
#if UseAutomaticLayout
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
_imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize);
// Set an intrinsic size for the image node
CGSize imageSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize);
[_imageNode setSizeWithCGSize:imageSize];
// Shrink the text node in case the image + text gonna be too wide
_textNode.flexShrink = YES;
// Configure stack
ASStackLayoutSpec *stackLayoutSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:kInnerPadding
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStart
children:_swappedTextAndImage ? @[_textNode, _imageNode] : @[_imageNode, _textNode]];
ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init];
stackSpec.direction = ASStackLayoutDirectionHorizontal;
stackSpec.spacing = kInnerPadding;
[stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]];
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding);
insetSpec.child = stackSpec;
return insetSpec;
// Add inset
return [ASInsetLayoutSpec
insetLayoutSpecWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding)
child:stackLayoutSpec];
}
// With box model, you don't need to override this method, unless you want to add custom logic.

View File

@@ -23,9 +23,7 @@
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{
return [ASLayout layoutWithLayoutableObject:self
constrainedSizeRange:constrainedSize
size:constrainedSize.max];
return [ASLayout layoutWithLayoutable:self size:constrainedSize.max];
}
- (void)fetchData

View File

@@ -52,13 +52,19 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:6.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:@[self.iconNode, self.countNode]];
ASStackLayoutSpec *mainStack =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:6.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_iconNode, _countNode]];
// set sizeRange to make width fixed to 60
ASRelativeSize min = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(0.0));
ASRelativeSize max = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(40.0));
mainStack.sizeRange = ASRelativeSizeRangeMake(min,max);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[mainStack]];
// Adjust size
mainStack.minWidth = ASDimensionMakeWithPoints(60.0);
mainStack.maxHeight = ASDimensionMakeWithPoints(40.0);
return mainStack;
}
@end

View File

@@ -66,13 +66,18 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:6.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:@[_iconNode, _countNode]];
// set sizeRange to make width fixed to 60
ASRelativeSize min = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(0.0));
ASRelativeSize max = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(40.0));
mainStack.sizeRange = ASRelativeSizeRangeMake(min, max);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[mainStack]];
ASStackLayoutSpec *mainStack =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:6.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_iconNode, _countNode]];
mainStack.minWidth = ASDimensionMakeWithPoints(60.0);
mainStack.maxHeight = ASDimensionMakeWithPoints(40.0);
return mainStack;
}
@end

View File

@@ -20,6 +20,7 @@
#import "TextStyles.h"
#import "LikesNode.h"
#import "CommentsNode.h"
#import "ASRelativeSize.h"
#define PostNodeDividerColor [UIColor lightGrayColor]
@@ -136,7 +137,8 @@
// User pic
_avatarNode = [[ASNetworkImageNode alloc] init];
_avatarNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
_avatarNode.preferredFrameSize = CGSizeMake(44, 44);
_avatarNode.width = ASDimensionMakeWithPoints(44);
_avatarNode.height = ASDimensionMakeWithPoints(44);
_avatarNode.cornerRadius = 22.0;
_avatarNode.URL = [NSURL URLWithString:_post.photo];
_avatarNode.imageModificationBlock = ^UIImage *(UIImage *image) {
@@ -214,7 +216,10 @@
// NOTE: This inset is not actually required by the layout, but is an example of the upward propogation of layoutable
// properties. Specifically, .flexGrow from the child is transferred to the inset spec so they can expand together.
// Without this capability, it would be required to set insetSpacer.flexGrow = YES;
ASInsetLayoutSpec *insetSpacer = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 0, 0, 0) child:spacer];
ASInsetLayoutSpec *insetSpacer =
[ASInsetLayoutSpec
insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)
child:spacer];
// Horizontal stack for name, username, via icon and time
NSMutableArray *layoutSpecChildren = [@[_nameNode, _usernameNode, insetSpacer] mutableCopy];
@@ -223,11 +228,23 @@
}
[layoutSpecChildren addObject:_timeNode];
ASStackLayoutSpec *nameStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:5.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:layoutSpecChildren];
ASStackLayoutSpec *nameStack =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:5.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:layoutSpecChildren];
nameStack.alignSelf = ASStackLayoutAlignSelfStretch;
// bottom controls horizontal stack
ASStackLayoutSpec *controlsStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:10 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:@[_likesNode, _commentsNode, _optionsNode]];
ASStackLayoutSpec *controlsStack =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:10
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_likesNode, _commentsNode, _optionsNode]];
// Add more gaps for control line
controlsStack.spacingAfter = 3.0;
@@ -237,26 +254,46 @@
[mainStackContent addObject:nameStack];
[mainStackContent addObject:_postNode];
if (![_post.media isEqualToString:@""]) {
CGFloat imageRatio = (_mediaNode.image != nil ? _mediaNode.image.size.height / _mediaNode.image.size.width : 0.5);
ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:_mediaNode];
imagePlace.spacingAfter = 3.0;
imagePlace.spacingBefore = 3.0;
[mainStackContent addObject:imagePlace];
if (![_post.media isEqualToString:@""]){
// Only add the media node if an image is present
if (_mediaNode.image != nil) {
CGFloat imageRatio = (_mediaNode.image != nil ? _mediaNode.image.size.height / _mediaNode.image.size.width : 0.5);
ASRatioLayoutSpec *imagePlace =
[ASRatioLayoutSpec
ratioLayoutSpecWithRatio:imageRatio
child:_mediaNode];
imagePlace.spacingAfter = 3.0;
imagePlace.spacingBefore = 3.0;
[mainStackContent addObject:imagePlace];
}
}
[mainStackContent addObject:controlsStack];
// Vertical spec of cell main content
ASStackLayoutSpec *contentSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:8.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:mainStackContent];
contentSpec.alignItems = ASStackLayoutAlignSelfStretch;
ASStackLayoutSpec *contentSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:8.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:mainStackContent];
contentSpec.flexShrink = YES;
// Horizontal spec for avatar
ASStackLayoutSpec *avatarContentSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:8.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[_avatarNode, contentSpec]];
ASStackLayoutSpec *avatarContentSpec =
[ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:8.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStart
children:@[_avatarNode, contentSpec]];
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) child:avatarContentSpec];
return [ASInsetLayoutSpec
insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10)
child:avatarContentSpec];
}

View File

@@ -55,7 +55,7 @@
{
[super viewDidLoad];
self.tableView = [[ASTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain asyncDataFetching:YES];
self.tableView = [[ASTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // SocialAppNode has its own separator
self.tableView.asyncDataSource = self;

View File

@@ -25,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
window.backgroundColor = UIColor.whiteColor()
window.rootViewController = ViewController()
window.rootViewController = UINavigationController(rootViewController: ViewController());
window.makeKeyAndVisible()
self.window = window
return true

View File

@@ -54,7 +54,8 @@ final class SpinnerNode: ASDisplayNode {
override init() {
super.init(viewBlock: { UIActivityIndicatorView(activityIndicatorStyle: .Gray) }, didLoadBlock: nil)
preferredFrameSize.height = 32
size.minHeight = ASDimensionMakeWithPoints(44.0)
}
override func didLoad() {

View File

@@ -127,14 +127,15 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate
/// (Pretend) fetches some new items and calls the
/// completion handler on the main thread.
private static func fetchDataWithCompletion(completion: (Int) -> Void) {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSTimeInterval(NSEC_PER_SEC) * 0.5))
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSTimeInterval(NSEC_PER_SEC) * 1.0))
dispatch_after(time, dispatch_get_main_queue()) {
let resultCount = Int(arc4random_uniform(20))
completion(resultCount)
}
}
private static func handleAction(action: Action, var fromState state: State) -> State {
private static func handleAction(action: Action, fromState state: State) -> State {
var state = state
switch action {
case .BeginBatchFetch:
state.fetchingMore = true

View File

@@ -68,7 +68,7 @@
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
elementNode.preferredFrameSize = _elementSize;
[elementNode setSizeWithCGSize:_elementSize];
elementNode.indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:_pageNumber];
return elementNode;
}

View File

@@ -71,17 +71,11 @@
_indexPathTextNode.attributedText = [[NSAttributedString alloc] initWithString:[indexPath description] attributes:nil];
}
//- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
//{
// ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[_indexPathTextNode]];
// stackSpec.flexGrow = YES;
// return stackSpec;
//}
- (void)layout
{
_indexPathTextNode.frame = self.bounds;
[super layout];
_indexPathTextNode.frame = self.bounds;
}
#if 0

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