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

View File

@@ -491,10 +491,17 @@
spec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentEdgeInsets child:spec]; spec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentEdgeInsets child:spec];
} }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) { 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]]; spec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack]];
} }
#pragma clang diagnostic pop
if (_backgroundImageNode.image) { if (_backgroundImageNode.image) {
spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec background:_backgroundImageNode]; spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec background:_backgroundImageNode];

View File

@@ -14,6 +14,38 @@
@interface ASDisplayNode (Deprecated) @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. * @abstract Called whenever the visiblity of the node changed.
* *

View File

@@ -118,6 +118,19 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize; - (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. * @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 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; - (CGSize)calculateSizeThatFits:(CGSize)constrainedSize;

View File

@@ -248,26 +248,7 @@ NS_ASSUME_NONNULL_BEGIN
/** @name Managing dimensions */ /** @name Managing dimensions */
/** /**
* @abstract Asks the node to measure and return the size that best fits its subnodes. * @abstract Asks the node to return a layout based on given size range.
*
* @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.
* *
* @param constrainedSize The minimum and maximum sizes the receiver should fit in. * @param constrainedSize The minimum and maximum sizes the receiver should fit in.
* *
@@ -281,8 +262,7 @@ NS_ASSUME_NONNULL_BEGIN
* *
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:] * @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 * @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; @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 */ /** @name Managing the nodes hierarchy */
@@ -625,7 +595,6 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* Convenience methods for debugging. * Convenience methods for debugging.
*/ */
@interface ASDisplayNode (Debugging) <ASLayoutableAsciiArtProtocol> @interface ASDisplayNode (Debugging) <ASLayoutableAsciiArtProtocol>
/** /**
@@ -838,6 +807,20 @@ NS_ASSUME_NONNULL_BEGIN
- (void)cancelLayoutTransition; - (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 @end
/* /*

View File

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

View File

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

View File

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

View File

@@ -1130,7 +1130,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height)); CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
} else { } else {
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0), constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0),
CGSizeMake(_nodesConstrainedWidth, FLT_MAX)); CGSizeMake(_nodesConstrainedWidth, CGFLOAT_MAX));
} }
return constrainedSize; 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. // 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. // To avoid premature optimization and making such assumption, as well as to keep ASTableView simple, re-measurement is strictly done on main.
[self beginUpdates]; [self beginUpdates];
CGSize calculatedSize = [[node measureWithSizeRange:constrainedSize] size]; const CGSize calculatedSize = [node layoutThatFits:constrainedSize].size;
node.frame = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height); node.frame = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height);
[self endUpdates]; [self endUpdates];
} }

View File

@@ -240,10 +240,17 @@ static NSString * const kRate = @"rate";
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
CGSize calculatedSize = constrainedSize; 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 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; calculatedSize = self.preferredFrameSize;
}
#pragma clang diagnostic pop
// Prevent crashes through if infinite width or height // Prevent crashes through if infinite width or height
if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) { if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) {
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode"); ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
@@ -251,8 +258,9 @@ static NSString * const kRate = @"rate";
} }
if (_playerNode) { if (_playerNode) {
_playerNode.preferredFrameSize = calculatedSize; _playerNode.width = ASDimensionMake(calculatedSize.width);
[_playerNode measure:calculatedSize]; _playerNode.height = ASDimensionMake(calculatedSize.height);
[_playerNode layoutThatFits:ASSizeRangeMake(CGSizeZero, calculatedSize)];
} }
return calculatedSize; return calculatedSize;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,6 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <CoreGraphics/CoreGraphics.h>
#import <UIKit/UIKit.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]; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2];
if (self.background) { if (self.background) {
// Size background to exactly the same size. // 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; backgroundLayout.position = CGPointZero;
[sublayouts addObject:backgroundLayout]; [sublayouts addObject:backgroundLayout];
} }
contentsLayout.position = CGPointZero; contentsLayout.position = CGPointZero;
[sublayouts addObject:contentsLayout]; [sublayouts addObject:contentsLayout];
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutable:self size:contentsLayout.size sublayouts:sublayouts];
constrainedSizeRange:constrainedSize
size:contentsLayout.size
sublayouts:sublayouts];
} }
- (void)setBackground:(id<ASLayoutable>)background - (void)setBackground:(id<ASLayoutable>)background

View File

@@ -11,58 +11,203 @@
#pragma once #pragma once
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <AsyncDisplayKit/ASBaseDefines.h> #import <AsyncDisplayKit/ASBaseDefines.h>
#import <AsyncDisplayKit/ASAssert.h>
/** A dimension relative to constraints to be provided in the future. */ ASDISPLAYNODE_INLINE BOOL ASPointsAreValidForLayout(CGFloat points)
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) { {
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. */ /** 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. */ /** Multiplied to a provided parent amount to resolve a final amount. */
ASRelativeDimensionTypeFraction, ASDimensionUnitFraction,
}; };
typedef struct { typedef struct {
ASRelativeDimensionType type; ASDimensionUnit unit;
CGFloat value; 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 { typedef struct {
CGSize min; CGSize min;
CGSize max; CGSize max;
} ASSizeRange; } 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 ASDISPLAYNODE_EXTERN_C_BEGIN
NS_ASSUME_NONNULL_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 #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 * 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 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); 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 NS_ASSUME_NONNULL_END
ASDISPLAYNODE_EXTERN_C_END ASDISPLAYNODE_EXTERN_C_END

View File

@@ -11,89 +11,164 @@
#import "ASDimension.h" #import "ASDimension.h"
#import "ASAssert.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) { // Handle empty string
ASDisplayNodeCAssertPositiveReal(@"Points", value); if (dimension.length == 0) {
} else if (type == ASRelativeDimensionTypeFraction) { return ASDimensionMake(ASDimensionUnitPoints, 0.0);
// 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);
} }
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); switch (dimension.unit) {
return ASRelativeDimensionMake(ASRelativeDimensionTypePoints, points); case ASDimensionUnitPoints:
}
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:
return [NSString stringWithFormat:@"%.0fpt", dimension.value]; return [NSString stringWithFormat:@"%.0fpt", dimension.value];
case ASRelativeDimensionTypeFraction: case ASDimensionUnitFraction:
return [NSString stringWithFormat:@"%.0f%%", dimension.value * 100.0]; 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) { return ASDimensionMake(ASDimensionUnitPoints, self.floatValue);
case ASRelativeDimensionTypePoints:
return dimension.value;
case ASRelativeDimensionTypeFraction:
return dimension.value * parent;
}
} }
- (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 #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 { struct _Range {
CGFloat min; CGFloat min;
CGFloat max; CGFloat max;
@@ -126,14 +201,17 @@ ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRan
return {{w.min, h.min}, {w.max, h.max}}; return {{w.min, h.min}, {w.max, h.max}};
} }
BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs) NSString *NSStringFromASSizeRange(ASSizeRange sizeRange)
{
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
}
NSString * NSStringFromASSizeRange(ASSizeRange sizeRange)
{ {
return [NSString stringWithFormat:@"<ASSizeRange: min=%@, max=%@>", return [NSString stringWithFormat:@"<ASSizeRange: min=%@, max=%@>",
NSStringFromCGSize(sizeRange.min), NSStringFromCGSize(sizeRange.min),
NSStringFromCGSize(sizeRange.max)]; 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 Inset will compute a new constrained size for it's child after applying insets and re-positioning
the child to respect the inset. 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 insetsX = (finiteOrZero(_insets.left) + finiteOrZero(_insets.right));
const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom)); const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom));
@@ -88,14 +95,12 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
} }
}; };
if (self.child == nil) { const CGSize insetParentSize = {
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing."); MAX(0, parentSize.width - insetsX),
return [ASLayout layoutWithLayoutableObject:self MAX(0, parentSize.height - insetsY)
constrainedSizeRange:constrainedSize };
size:CGSizeZero];
}
ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize]; ASLayout *sublayout = [self.child layoutThatFits:insetConstrainedSize parentSize:insetParentSize];
const CGSize computedSize = ASSizeRangeClamp(constrainedSize, { const CGSize computedSize = ASSizeRangeClamp(constrainedSize, {
finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width), 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); sublayout.position = CGPointMake(x, y);
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutable:self size:computedSize sublayouts:@[sublayout]];
constrainedSizeRange:constrainedSize
size:computedSize
sublayouts:@[sublayout]];
} }
@end @end

View File

@@ -17,10 +17,29 @@
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
ASDISPLAYNODE_EXTERN_C_BEGIN
extern CGPoint const CGPointNull; extern CGPoint const CGPointNull;
extern BOOL CGPointIsNull(CGPoint point); 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). * 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 * 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 * The type of ASLayoutable that created this layout
*/ */
@property (nonatomic, readonly) ASLayoutableType type; @property (nonatomic, assign, readonly) ASLayoutableType type;
/** /**
* Size of the current layout * Size of the current layout
*/ */
@property (nonatomic, readonly) CGSize size; @property (nonatomic, assign, readonly) CGSize size;
/** /**
* Position in parent. Default to CGPointNull. * Position in parent. Default to CGPointNull.
* *
* @discussion When being used as a sublayout, this property must not equal CGPointNull. * @discussion When being used as a sublayout, this property must not equal CGPointNull.
*/ */
@property (nonatomic, readwrite) CGPoint position; @property (nonatomic, assign, readwrite) CGPoint position;
/**
* The size range that was use to determine the size of the layout.
*/
@property (nonatomic, readonly) ASSizeRange constrainedSizeRange;
/** /**
* Array of ASLayouts. Each must have a valid non-null position. * Array of ASLayouts. Each must have a valid non-null position.
*/ */
@property (nonatomic, readonly) NSArray<ASLayout *> *sublayouts; @property (nonatomic, copy, readonly) NSArray<ASLayout *> *sublayouts;
/**
* Mark the layout dirty for future regeneration.
*/
@property (nonatomic, getter=isDirty) BOOL dirty;
/** /**
* @abstract Returns a valid frame for the current layout computed with the size and position. * @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. * @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 * Designated initializer
*/ */
- (instancetype)initWithLayoutableObject:(id<ASLayoutable>)layoutableObject - (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size
size:(CGSize)size position:(CGPoint)position
position:(CGPoint)position sublayouts:(nullable NSArray<ASLayout *> *)sublayouts NS_DESIGNATED_INITIALIZER;
sublayouts:(NSArray *)sublayouts NS_DESIGNATED_INITIALIZER;
/** /**
* Convenience class initializer for layout construction. * 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 size The size of this layout.
* @param position The position of this layout within its parent (if available). * @param position The position of this layout within its parent (if available).
* @param sublayouts Sublayouts belong to the new layout. * @param sublayouts Sublayouts belong to the new layout.
*/ */
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject + (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size
size:(CGSize)size position:(CGPoint)position
position:(CGPoint)position sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
/** /**
* Convenience initializer that has CGPointNull position. * 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 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. * or for creating a sublayout of which the position is yet to be determined.
* *
* @param layoutableObject The backing ASLayoutable object. * @param layoutable The backing ASLayoutable object.
* @param size The size of this layout. * @param size The size of this layout.
* @param sublayouts Sublayouts belong to the new layout. * @param sublayouts Sublayouts belong to the new layout.
*/ */
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject + (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size
size:(CGSize)size sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
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 * 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. * or a sublayout of which the position is yet to be determined.
* *
* @param layoutableObject The backing ASLayoutable object. * @param layoutable The backing ASLayoutable object.
* @param size The size of this layout. * @param size The size of this layout.
*/ */
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject + (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size;
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;
/** /**
* Convenience initializer that creates a layout based on the values of the given layout, with a new position * 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; + (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position;
@@ -145,6 +138,14 @@ extern BOOL CGPointIsNull(CGPoint point);
@end @end
@interface ASLayout (Unavailable)
- (instancetype)init __unavailable;
@end
#pragma mark - Debugging
@interface ASLayout (Debugging) @interface ASLayout (Debugging)
/** /**
@@ -154,10 +155,4 @@ extern BOOL CGPointIsNull(CGPoint point);
@end @end
@interface ASLayout (Unavailable)
- (instancetype)init __unavailable;
@end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -51,38 +51,37 @@ static inline NSString * descriptionIndents(NSUInteger indents)
@dynamic frame, type; @dynamic frame, type;
- (instancetype)initWithLayoutableObject:(id<ASLayoutable>)layoutableObject - (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size
size:(CGSize)size position:(CGPoint)position
position:(CGPoint)position sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
sublayouts:(NSArray *)sublayouts
{ {
NSParameterAssert(layoutable);
self = [super init]; self = [super init];
if (self) { if (self) {
NSParameterAssert(layoutableObject);
#if DEBUG #if DEBUG
for (ASLayout *sublayout in sublayouts) { for (ASLayout *sublayout in sublayouts) {
ASDisplayNodeAssert(CGPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout."); ASDisplayNodeAssert(CGPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout.");
} }
#endif #endif
_layoutableObject = layoutableObject; _layoutable = layoutable;
if (!isValidForLayout(size.width) || !isValidForLayout(size.height)) { if (!ASIsCGSizeValidForLayout(size)) {
ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Production will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutableObject); 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; size = CGSizeZero;
} else { } else {
size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height)); size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
} }
_constrainedSizeRange = sizeRange;
_size = size; _size = size;
_dirty = NO;
if (CGPointIsNull(position) == NO) { if (CGPointIsNull(position) == NO) {
_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y)); _position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y));
} else { } else {
_position = position; _position = position;
} }
_sublayouts = sublayouts != nil ? [sublayouts copy] : @[]; _sublayouts = sublayouts != nil ? [sublayouts copy] : @[];
_flattened = NO; _flattened = NO;
} }
@@ -97,61 +96,41 @@ static inline NSString * descriptionIndents(NSUInteger indents)
#pragma mark - Class Constructors #pragma mark - Class Constructors
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject + (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size
size:(CGSize)size position:(CGPoint)position
position:(CGPoint)position sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
sublayouts:(NSArray *)sublayouts
{ {
return [[self alloc] initWithLayoutableObject:layoutableObject return [[self alloc] initWithLayoutable:layoutable
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
size:size size:size
position:CGPointNull position:position
sublayouts:sublayouts]; sublayouts:sublayouts];
} }
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject + (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
constrainedSizeRange:(ASSizeRange)sizeRange size:(CGSize)size
size:(CGSize)size sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
{ {
return [self layoutWithLayoutableObject:layoutableObject return [self layoutWithLayoutable:layoutable
constrainedSizeRange:sizeRange size:size
size:size position:CGPointNull
position:CGPointNull sublayouts:sublayouts];
sublayouts:nil];
} }
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject + (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable size:(CGSize)size
constrainedSizeRange:(ASSizeRange)sizeRange
size:(CGSize)size
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
{ {
return [self layoutWithLayoutableObject:layoutableObject return [self layoutWithLayoutable:layoutable
constrainedSizeRange:sizeRange size:size
size:size position:CGPointNull
position:CGPointNull sublayouts:nil];
sublayouts:sublayouts];
} }
+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position + (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position
{ {
return [self layoutWithLayoutableObject:layout.layoutableObject return [self layoutWithLayoutable:layout.layoutable
constrainedSizeRange:layout.constrainedSizeRange size:layout.size
size:layout.size position:position
position:position sublayouts:layout.sublayouts];
sublayouts:layout.sublayouts];
} }
#pragma mark - Layout Flattening #pragma mark - Layout Flattening
@@ -186,17 +165,14 @@ static inline NSString * descriptionIndents(NSUInteger indents)
} }
} }
return [ASLayout layoutWithLayoutableObject:_layoutableObject return [ASLayout layoutWithLayoutable:_layoutable size:_size sublayouts:flattenedSublayouts];
constrainedSizeRange:_constrainedSizeRange
size:_size
sublayouts:flattenedSublayouts];
} }
#pragma mark - Accessors #pragma mark - Accessors
- (ASLayoutableType)type - (ASLayoutableType)type
{ {
return _layoutableObject.layoutableType; return _layoutable.layoutableType;
} }
- (CGRect)frame - (CGRect)frame
@@ -231,8 +207,8 @@ static inline NSString * descriptionIndents(NSUInteger indents)
- (NSString *)description - (NSString *)description
{ {
return [NSString stringWithFormat:@"<<ASLayout: %p>, layoutable = %@, position = %@; size = %@; constrainedSizeRange = %@>", return [NSString stringWithFormat:@"<<ASLayout: %p>, layoutable = %@, position = %@; size = %@;>",
self, self.layoutableObject, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size), NSStringFromASSizeRange(self.constrainedSizeRange)]; self, self.layoutable, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size)/*, NSStringFromASSizeRange(self.constrainedSize)*/];
} }
- (NSString *)recursiveDescription - (NSString *)recursiveDescription
@@ -253,3 +229,18 @@ static inline NSString * descriptionIndents(NSUInteger indents)
} }
@end @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 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> @interface ASLayoutSpec : NSObject <ASLayoutable>
/** /**
@@ -88,6 +90,28 @@ NS_ASSUME_NONNULL_BEGIN
@end @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> @interface ASLayoutSpec (Debugging) <ASLayoutableAsciiArtProtocol>
/** /**
* Used by other layout specs to create ascii art debug strings * Used by other layout specs to create ascii art debug strings

View File

@@ -11,6 +11,7 @@
#import "ASLayoutSpec.h" #import "ASLayoutSpec.h"
#import "ASAssert.h" #import "ASAssert.h"
#import "ASInternalHelpers.h"
#import "ASEnvironmentInternal.h" #import "ASEnvironmentInternal.h"
#import "ASLayout.h" #import "ASLayout.h"
@@ -24,25 +25,46 @@
typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASChildMap; typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASChildMap;
@interface ASLayoutSpec() { @interface ASLayoutSpec() {
ASEnvironmentState _environmentState;
ASDN::RecursiveMutex __instanceLock__; ASDN::RecursiveMutex __instanceLock__;
ASLayoutableSize _size;
ASEnvironmentState _environmentState;
ASChildMap _children; ASChildMap _children;
} }
@end @end
@implementation ASLayoutSpec @implementation ASLayoutSpec
// these dynamic properties all defined in ASLayoutOptionsPrivate.m // Dynamic properties for ASLayoutables
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, @dynamic layoutableType, size;
alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType; // 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; @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 - (instancetype)init
{ {
if (!(self = [super init])) { if (!(self = [super init])) {
return nil; return nil;
} }
_isMutable = YES; _isMutable = YES;
_size = ASLayoutableSizeMake();
_environmentState = ASEnvironmentStateMakeDefault(); _environmentState = ASEnvironmentStateMakeDefault();
return self; return self;
} }
@@ -57,13 +79,54 @@ typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASCh
return YES; 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 #pragma mark - Layout
// Deprecated
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
{ {
return [ASLayout layoutWithLayoutableObject:self return [self layoutThatFits:constrainedSize];
constrainedSizeRange:constrainedSize }
size:constrainedSize.min];
- (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 - (id<ASLayoutable>)finalLayoutable
@@ -233,6 +296,37 @@ ASEnvironmentLayoutExtensibilityForwarding
@end @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) @implementation ASLayoutSpec (Debugging)
#pragma mark - ASLayoutableAsciiArtProtocol #pragma mark - ASLayoutableAsciiArtProtocol

View File

@@ -65,28 +65,14 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
- (void)validateLayout:(ASLayout *)layout - (void)validateLayout:(ASLayout *)layout
{ {
for (ASLayout *sublayout in layout.sublayouts) { for (ASLayout *sublayout in layout.sublayouts) {
id<ASLayoutable> layoutable = layout.layoutableObject; id<ASLayoutable> layoutable = layout.layoutable;
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutableObject; id<ASLayoutable> sublayoutLayoutable = sublayout.layoutable;
NSString *assertMessage = nil; NSString *assertMessage = nil;
Class stackContainerClass = [ASStaticLayoutSpec class]; Class stackContainerClass = [ASStaticLayoutSpec class];
// Check for default sizeRange and layoutPosition // Check for default layoutPosition
ASRelativeSizeRange sizeRange = sublayoutLayoutable.sizeRange; if (!CGPointEqualToPoint(sublayoutLayoutable.layoutPosition, CGPointZero)) {
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)) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(layoutPosition), sublayoutLayoutable, stackContainerClass); assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(layoutPosition), sublayoutLayoutable, stackContainerClass);
} }
@@ -110,9 +96,9 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
- (void)validateLayout:(ASLayout *)layout - (void)validateLayout:(ASLayout *)layout
{ {
id<ASLayoutable> layoutable = layout.layoutableObject; id<ASLayoutable> layoutable = layout.layoutable;
for (ASLayout *sublayout in layout.sublayouts) { for (ASLayout *sublayout in layout.sublayouts) {
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutableObject; id<ASLayoutable> sublayoutLayoutable = sublayout.layoutable;
NSString *assertMessage = nil; NSString *assertMessage = nil;
Class stackContainerClass = [ASStackLayoutSpec class]; Class stackContainerClass = [ASStackLayoutSpec class];
@@ -126,7 +112,7 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexGrow), sublayoutLayoutable, stackContainerClass); assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexGrow), sublayoutLayoutable, stackContainerClass);
} else if (sublayoutLayoutable.flexShrink == YES) { } else if (sublayoutLayoutable.flexShrink == YES) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexShrink), sublayoutLayoutable, stackContainerClass); assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexShrink), sublayoutLayoutable, stackContainerClass);
} else if (!ASRelativeDimensionEqualToRelativeDimension(sublayoutLayoutable.flexBasis, ASRelativeDimensionUnconstrained) ) { } else if (!ASDimensionEqualToDimension(sublayoutLayoutable.flexBasis, ASDimensionAuto) ) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexBasis), sublayoutLayoutable, stackContainerClass); assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexBasis), sublayoutLayoutable, stackContainerClass);
} else if (sublayoutLayoutable.alignSelf != ASStackLayoutAlignSelfAuto) { } else if (sublayoutLayoutable.alignSelf != ASStackLayoutAlignSelfAuto) {
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(alignSelf), sublayoutLayoutable, stackContainerClass); assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(alignSelf), sublayoutLayoutable, stackContainerClass);

View File

@@ -9,7 +9,6 @@
// //
#import <AsyncDisplayKit/ASDimension.h> #import <AsyncDisplayKit/ASDimension.h>
#import <AsyncDisplayKit/ASRelativeSize.h>
#import <AsyncDisplayKit/ASStackLayoutDefines.h> #import <AsyncDisplayKit/ASStackLayoutDefines.h>
#import <AsyncDisplayKit/ASStackLayoutable.h> #import <AsyncDisplayKit/ASStackLayoutable.h>
#import <AsyncDisplayKit/ASStaticLayoutable.h> #import <AsyncDisplayKit/ASStaticLayoutable.h>
@@ -21,6 +20,13 @@
@class ASLayout; @class ASLayout;
@class ASLayoutSpec; @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) { typedef NS_ENUM(NSUInteger, ASLayoutableType) {
ASLayoutableTypeLayoutSpec, ASLayoutableTypeLayoutSpec,
ASLayoutableTypeDisplayNode ASLayoutableTypeDisplayNode
@@ -49,24 +55,142 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* @abstract Returns type of layoutable * @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. * @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. * @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 - Layout options from the Layoutable Protocols
#pragma mark - ASStackLayoutable #pragma mark - ASStackLayoutable
/** /**
* @abstract Additional space to place before this object in the stacking direction. * @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. * @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. * 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 * @abstract Orientation of the object along cross axis, overriding alignItems
@@ -115,15 +239,28 @@ NS_ASSUME_NONNULL_BEGIN
*/ */
@property (nonatomic, readwrite) CGFloat descender; @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; @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 @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

View File

@@ -16,6 +16,9 @@
#import <map> #import <map>
CGFloat const ASLayoutableParentDimensionUndefined = NAN;
CGSize const ASLayoutableParentSizeUndefined = {ASLayoutableParentDimensionUndefined, ASLayoutableParentDimensionUndefined};
int32_t const ASLayoutableContextInvalidTransitionID = 0; int32_t const ASLayoutableContextInvalidTransitionID = 0;
int32_t const ASLayoutableContextDefaultTransitionID = ASLayoutableContextInvalidTransitionID + 1; 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. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <Foundation/Foundation.h> #import "ASDimension.h"
@class ASLayoutSpec; @class ASLayoutSpec;
@protocol ASLayoutable; @protocol ASLayoutable;
@@ -40,6 +40,11 @@ extern void ASLayoutableClearCurrentContext();
*/ */
@protocol ASLayoutablePrivate <NSObject> @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 * @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 * 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 @end
#pragma mark - ASLayoutableForwarding
#pragma mark - ASLayoutOptionsForwarding
/** /**
* Both an ASDisplayNode and an ASLayoutSpec conform to ASLayoutable. There are several properties * 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. * 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. * 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 \ #define ASEnvironmentLayoutOptionsForwarding \
- (void)propagateUpLayoutOptionsState\ - (void)propagateUpLayoutOptionsState\
{\ {\
@@ -132,12 +232,12 @@ extern void ASLayoutableClearCurrentContext();
[self propagateUpLayoutOptionsState];\ [self propagateUpLayoutOptionsState];\
}\ }\
\ \
- (ASRelativeDimension)flexBasis\ - (ASDimension)flexBasis\
{\ {\
return _environmentState.layoutOptionsState.flexBasis;\ return _environmentState.layoutOptionsState.flexBasis;\
}\ }\
\ \
- (void)setFlexBasis:(ASRelativeDimension)flexBasis\ - (void)setFlexBasis:(ASDimension)flexBasis\
{\ {\
_environmentState.layoutOptionsState.flexBasis = flexBasis;\ _environmentState.layoutOptionsState.flexBasis = flexBasis;\
[self propagateUpLayoutOptionsState];\ [self propagateUpLayoutOptionsState];\
@@ -176,17 +276,6 @@ extern void ASLayoutableClearCurrentContext();
[self propagateUpLayoutOptionsState];\ [self propagateUpLayoutOptionsState];\
}\ }\
\ \
- (ASRelativeSizeRange)sizeRange\
{\
return _environmentState.layoutOptionsState.sizeRange;\
}\
\
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\
{\
_environmentState.layoutOptionsState.sizeRange = sizeRange;\
[self propagateUpLayoutOptionsState];\
}\
\
- (CGPoint)layoutPosition\ - (CGPoint)layoutPosition\
{\ {\
return _environmentState.layoutOptionsState.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. 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; contentsLayout.position = CGPointZero;
NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout]; NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout];
if (self.overlay) { 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; overlayLayout.position = CGPointZero;
[sublayouts addObject:overlayLayout]; [sublayouts addObject:overlayLayout];
} }
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutable:self size:contentsLayout.size sublayouts:sublayouts];
constrainedSizeRange:constrainedSize
size:contentsLayout.size
sublayouts:sublayouts];
} }
@end @end

View File

@@ -47,16 +47,18 @@
_ratio = ratio; _ratio = ratio;
} }
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{ {
std::vector<CGSize> sizeOptions; 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, { sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
constrainedSize.max.width, constrainedSize.max.width,
ASFloorPixelValue(_ratio * 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, { sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
ASFloorPixelValue(constrainedSize.max.height / _ratio), ASFloorPixelValue(constrainedSize.max.height / _ratio),
constrainedSize.max.height 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. // 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); 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; sublayout.position = CGPointZero;
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutable:self size:sublayout.size sublayouts:@[sublayout]];
constrainedSizeRange:constrainedSize
size:sublayout.size
sublayouts:@[sublayout]];
} }
@end @end

View File

@@ -52,11 +52,15 @@
_sizingOption = sizingOption; _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 = { CGSize size = {
constrainedSize.max.width, isinf(constrainedSize.max.width) || !ASPointsAreValidForLayout(constrainedSize.max.width) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.width,
constrainedSize.max.height isinf(constrainedSize.max.height) || !ASPointsAreValidForLayout(constrainedSize.max.height) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.height
}; };
BOOL reduceWidth = (_horizontalPosition & ASRelativeLayoutSpecPositionCenter) != 0 || BOOL reduceWidth = (_horizontalPosition & ASRelativeLayoutSpecPositionCenter) != 0 ||
@@ -70,7 +74,8 @@
reduceWidth ? 0 : constrainedSize.min.width, reduceWidth ? 0 : constrainedSize.min.width,
reduceHeight ? 0 : constrainedSize.min.height, 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 // If we have an undetermined height or width, use the child size to define the layout
// size // size
@@ -94,10 +99,7 @@
ASRoundPixelValue((size.height - sublayout.size.height) * yPosition) ASRoundPixelValue((size.height - sublayout.size.height) * yPosition)
}; };
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutable:self size:size sublayouts:@[sublayout]];
constrainedSizeRange:constrainedSize
size:size
sublayouts:@[sublayout]];
} }
- (CGFloat)proportionOfAxisForAxisPosition:(ASRelativeLayoutSpecPosition)position - (CGFloat)proportionOfAxisForAxisPosition:(ASRelativeLayoutSpecPosition)position

View File

@@ -8,68 +8,4 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <UIKit/UIKit.h> // TODO: layout: Remove file
#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

View File

@@ -8,84 +8,4 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import "ASRelativeSize.h" // TODO: layout: Remove file
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));
}

View File

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

View File

@@ -44,10 +44,10 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
* @abstract Specifies the initial size in the stack dimension for this object. * @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. * 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 * @abstract Orientation of the object along cross axis, overriding alignItems

View File

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

View File

@@ -34,42 +34,46 @@
return self; 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; NSArray *children = self.children;
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count]; NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
for (id<ASLayoutable> child in children) { for (id<ASLayoutable> child in children) {
CGPoint layoutPosition = child.layoutPosition; CGPoint layoutPosition = child.layoutPosition;
CGSize autoMaxSize = CGSizeMake(maxConstrainedSize.width - layoutPosition.x, CGSize autoMaxSize = {
maxConstrainedSize.height - layoutPosition.y); constrainedSize.max.width - layoutPosition.x,
constrainedSize.max.height - layoutPosition.y
};
const ASSizeRange childConstraint = ASLayoutableSizeResolveAutoSize(child.size, size, {{0,0}, autoMaxSize});
ASRelativeSizeRange childSizeRange = child.sizeRange; ASLayout *sublayout = [child layoutThatFits:childConstraint parentSize:size];
BOOL childIsUnconstrained = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, childSizeRange);
ASSizeRange childConstraint = childIsUnconstrained ? ASSizeRangeMake({0, 0}, autoMaxSize)
: ASRelativeSizeRangeResolve(childSizeRange, maxConstrainedSize);
ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
sublayout.position = layoutPosition; sublayout.position = layoutPosition;
[sublayouts addObject:sublayout]; [sublayouts addObject:sublayout];
} }
CGSize size = CGSizeMake(constrainedSize.min.width, constrainedSize.min.height); if (isnan(size.width)) {
size.width = constrainedSize.min.width;
for (ASLayout *sublayout in sublayouts) { for (ASLayout *sublayout in sublayouts) {
CGPoint sublayoutPosition = sublayout.position; size.width = MAX(size.width, sublayout.position.x + sublayout.size.width);
CGSize sublayoutSize = sublayout.size; }
size.width = MAX(size.width, sublayoutPosition.x + sublayoutSize.width);
size.height = MAX(size.height, sublayoutPosition.y + sublayoutSize.height);
} }
return [ASLayout layoutWithLayoutableObject:self if (isnan(size.height)) {
constrainedSizeRange:constrainedSize size.height = constrainedSize.min.height;
size:ASSizeRangeClamp(constrainedSize, size) for (ASLayout *sublayout in sublayouts) {
sublayouts:sublayouts]; size.height = MAX(size.height, sublayout.position.y + sublayout.size.height);
}
}
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:sublayouts];
} }
@end @end

View File

@@ -8,8 +8,6 @@
// of patent rights can be found in the PATENTS file in the same directory. // of patent rights can be found in the PATENTS file in the same directory.
// //
#import <AsyncDisplayKit/ASRelativeSize.h>
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
/** /**
@@ -18,11 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
@protocol ASStaticLayoutable @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; @property (nonatomic, assign) CGPoint layoutPosition;
@end @end

View File

@@ -43,8 +43,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4 ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4
}; };
@class _ASDisplayNodePosition;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification; FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp; FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp;
@@ -104,6 +102,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
@protected @protected
ASDisplayNode * __weak _supernode; ASDisplayNode * __weak _supernode;
ASLayoutableSize _size;
CGSize _preferredFrameSize;
ASSentinel *_displaySentinel; ASSentinel *_displaySentinel;
@@ -114,21 +115,20 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
CGFloat _contentsScaleForDisplay; CGFloat _contentsScaleForDisplay;
ASEnvironmentState _environmentState; ASEnvironmentState _environmentState;
ASLayout *_calculatedLayout;
UIEdgeInsets _hitTestSlop; UIEdgeInsets _hitTestSlop;
NSMutableArray *_subnodes; NSMutableArray *_subnodes;
// Main thread only // Main thread only
_ASTransitionContext *_pendingLayoutTransitionContext;
BOOL _automaticallyManagesSubnodes; BOOL _automaticallyManagesSubnodes;
_ASTransitionContext *_pendingLayoutTransitionContext;
NSTimeInterval _defaultLayoutTransitionDuration; NSTimeInterval _defaultLayoutTransitionDuration;
NSTimeInterval _defaultLayoutTransitionDelay; NSTimeInterval _defaultLayoutTransitionDelay;
UIViewAnimationOptions _defaultLayoutTransitionOptions; UIViewAnimationOptions _defaultLayoutTransitionOptions;
int32_t _pendingTransitionID; int32_t _pendingTransitionID;
ASLayoutTransition *_pendingLayoutTransition; ASLayoutTransition *_pendingLayoutTransition;
std::shared_ptr<ASDisplayNodeLayout> _calculatedDisplayNodeLayout;
ASDisplayNodeViewBlock _viewBlock; ASDisplayNodeViewBlock _viewBlock;
ASDisplayNodeLayerBlock _layerBlock; ASDisplayNodeLayerBlock _layerBlock;
@@ -184,7 +184,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
// Swizzle to extend the builtin functionality with custom logic // Swizzle to extend the builtin functionality with custom logic
- (BOOL)__shouldLoadViewOrLayer; - (BOOL)__shouldLoadViewOrLayer;
- (BOOL)__shouldSize;
/** /**
Invoked before a call to setNeedsLayout to the underlying view 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) { if (parentLayoutOptionsState.flexGrow == defaultState.flexGrow) {
parentLayoutOptionsState.flexGrow = layoutOptionsState.flexGrow; parentLayoutOptionsState.flexGrow = layoutOptionsState.flexGrow;
} }
if (ASRelativeDimensionEqualToRelativeDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) { if (ASDimensionEqualToDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) {
parentLayoutOptionsState.flexBasis = layoutOptionsState.flexBasis; parentLayoutOptionsState.flexBasis = layoutOptionsState.flexBasis;
} }
if (parentLayoutOptionsState.alignSelf == defaultState.alignSelf) { if (parentLayoutOptionsState.alignSelf == defaultState.alignSelf) {
@@ -154,10 +154,6 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme
parentLayoutOptionsState.ascender = layoutOptionsState.ascender; 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)) { if (CGPointEqualToPoint(parentLayoutOptionsState.layoutPosition, defaultState.layoutPosition)) {
// For now it is unclear if we should be up-propagating sizeRange or layoutPosition. // For now it is unclear if we should be up-propagating sizeRange or layoutPosition.
// parentLayoutOptionsState.layoutPosition = layoutOptionsState.layoutPosition; // parentLayoutOptionsState.layoutPosition = layoutOptionsState.layoutPosition;

View File

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

View File

@@ -12,9 +12,13 @@
#import "ASDimension.h" #import "ASDimension.h"
#import "_ASTransitionContext.h" #import "_ASTransitionContext.h"
#import "ASDisplayNodeLayout.h"
#import <memory>
NS_ASSUME_NONNULL_BEGIN
@class ASDisplayNode; @class ASDisplayNode;
@class ASLayout;
@interface ASLayoutTransition : NSObject <_ASTransitionContextLayoutDelegate> @interface ASLayoutTransition : NSObject <_ASTransitionContextLayoutDelegate>
@@ -26,12 +30,12 @@
/** /**
* Previous layout to transition from * 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 * 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 * Returns if the layout transition needs to happen synchronously
@@ -41,8 +45,9 @@
/** /**
* Returns a newly initialized layout transition * Returns a newly initialized layout transition
*/ */
- (instancetype)initWithNode:(ASDisplayNode *)node pendingLayout:(ASLayout *)pendingLayout previousLayout:(ASLayout *)previousLayout NS_DESIGNATED_INITIALIZER; - (instancetype)initWithNode:(ASDisplayNode *)node
- (instancetype)init NS_UNAVAILABLE; 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 * Insert and remove subnodes that where added or removed between the previousLayout and the pendingLayout
@@ -60,3 +65,11 @@
- (void)applySubnodeRemovals; - (void)applySubnodeRemovals;
@end @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(); layout = queue.front();
queue.pop(); queue.pop();
if (layout.layoutableObject.canLayoutAsynchronous == NO) { if (layout.layoutable.canLayoutAsynchronous == NO) {
return NO; return NO;
} }
@@ -58,8 +58,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
} }
- (instancetype)initWithNode:(ASDisplayNode *)node - (instancetype)initWithNode:(ASDisplayNode *)node
pendingLayout:(ASLayout *)pendingLayout pendingLayout:(std::shared_ptr<ASDisplayNodeLayout>)pendingLayout
previousLayout:(ASLayout *)previousLayout previousLayout:(std::shared_ptr<ASDisplayNodeLayout>)previousLayout
{ {
self = [super init]; self = [super init];
if (self) { if (self) {
@@ -72,10 +72,16 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
return self; return self;
} }
- (instancetype)init
{
ASDisplayNodeAssert(NO, @"Use the designated initializer");
return [self init];
}
- (BOOL)isSynchronous - (BOOL)isSynchronous
{ {
ASDN::MutexSharedLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
return !ASLayoutCanTransitionAsynchronous(_pendingLayout); return !ASLayoutCanTransitionAsynchronous(_pendingLayout->layout);
} }
- (void)commitTransition - (void)commitTransition
@@ -112,23 +118,27 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
if (_calculatedSubnodeOperations) { if (_calculatedSubnodeOperations) {
return; return;
} }
if (_previousLayout) {
ASLayout *previousLayout = _previousLayout->layout;
ASLayout *pendingLayout = _pendingLayout->layout;
if (previousLayout) {
NSIndexSet *insertions, *deletions; NSIndexSet *insertions, *deletions;
[_previousLayout.sublayouts asdk_diffWithArray:_pendingLayout.sublayouts [previousLayout.sublayouts asdk_diffWithArray:pendingLayout.sublayouts
insertions:&insertions insertions:&insertions
deletions:&deletions deletions:&deletions
compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) { compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) {
return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject); return ASObjectIsEqual(lhs.layoutable, rhs.layoutable);
}]; }];
findNodesInLayoutAtIndexes(_pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions); findNodesInLayoutAtIndexes(pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions);
findNodesInLayoutAtIndexesWithFilteredNodes(_previousLayout, findNodesInLayoutAtIndexesWithFilteredNodes(previousLayout,
deletions, deletions,
_insertedSubnodes, _insertedSubnodes,
&_removedSubnodes, &_removedSubnodes,
&_removedSubnodePositions); &_removedSubnodePositions);
} else { } else {
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_pendingLayout.sublayouts count])]; NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pendingLayout.sublayouts count])];
findNodesInLayoutAtIndexes(_pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions); findNodesInLayoutAtIndexes(pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions);
_removedSubnodes = nil; _removedSubnodes = nil;
} }
_calculatedSubnodeOperations = YES; _calculatedSubnodeOperations = YES;
@@ -160,9 +170,9 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
{ {
ASDN::MutexSharedLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
return _previousLayout; return _previousLayout->layout;
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
return _pendingLayout; return _pendingLayout->layout;
} else { } else {
return nil; return nil;
} }
@@ -172,9 +182,9 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
{ {
ASDN::MutexSharedLocker l(__instanceLock__); ASDN::MutexSharedLocker l(__instanceLock__);
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) { if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
return _previousLayout.constrainedSizeRange; return _previousLayout->constrainedSize;
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) { } else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
return _pendingLayout.constrainedSizeRange; return _pendingLayout->constrainedSize;
} else { } else {
return ASSizeRangeMake(CGSizeZero, CGSizeZero); return ASSizeRangeMake(CGSizeZero, CGSizeZero);
} }
@@ -212,7 +222,7 @@ static inline void findNodesInLayoutAtIndexesWithFilteredNodes(ASLayout *layout,
for (ASLayout *sublayout in layout.sublayouts) { for (ASLayout *sublayout in layout.sublayouts) {
if (idx > lastIndex) { break; } if (idx > lastIndex) { break; }
if (idx >= firstIndex && [indexes containsIndex:idx]) { 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"); 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 // Ignore the odd case in which a non-node sublayout is accessed and the type cast fails
if (node != nil) { if (node != nil) {

View File

@@ -15,7 +15,7 @@
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
const ASLayout *layout) { const ASLayout *layout) {
__weak id<ASLayoutable> child = layout.layoutableObject; __weak id<ASLayoutable> child = layout.layoutable;
switch (style.alignItems) { switch (style.alignItems) {
case ASStackLayoutAlignItemsBaselineFirst: case ASStackLayoutAlignItemsBaselineFirst:
return child.ascender; return child.ascender;
@@ -33,7 +33,7 @@ static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
const CGFloat maxBaseline) const CGFloat maxBaseline)
{ {
if (style.direction == ASStackLayoutDirectionHorizontal) { if (style.direction == ASStackLayoutDirectionHorizontal) {
__weak id<ASLayoutable> child = l.layoutableObject; __weak id<ASLayoutable> child = l.layoutable;
switch (style.alignItems) { switch (style.alignItems) {
case ASStackLayoutAlignItemsBaselineFirst: case ASStackLayoutAlignItemsBaselineFirst:
return maxAscender - child.ascender; 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. 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){ 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. 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; CGPoint p = CGPointZero;
BOOL first = YES; BOOL first = YES;
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{ 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); p = p + directionPoint(style.direction, child.spacingBefore, 0);
if (first) { if (first) {
// if this is the first item use the previously computed start point // 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){ 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; 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}; return {stackedChildren, crossSize, maxAscender, minDescender};
} }

View File

@@ -23,15 +23,16 @@ static ASLayout *crossChildLayout(const id<ASLayoutable> child,
const CGFloat stackMin, const CGFloat stackMin,
const CGFloat stackMax, const CGFloat stackMax,
const CGFloat crossMin, const CGFloat crossMin,
const CGFloat crossMax) const CGFloat crossMax,
const CGSize size)
{ {
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems); const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
// stretched children will have a cross dimension of at least crossMin // stretched children will have a cross dimension of at least crossMin
const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0; const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0;
const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax); 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); 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 @param style the layout style of the overall stack layout
*/ */
static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedItem> &layouts, 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 // Find the maximum cross dimension size among child layouts
const auto it = std::max_element(layouts.begin(), layouts.end(), 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); const CGFloat childCrossMax = it == layouts.end() ? 0 : crossDimension(style.direction, it->layout.size);
for (auto &l : layouts) { for (auto &l : layouts) {
const id<ASLayoutable> child = l.child; const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems);
const CGSize size = l.layout.size;
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
const CGFloat cross = crossDimension(style.direction, size); const CGFloat cross = crossDimension(style.direction, l.layout.size);
const CGFloat stack = stackDimension(style.direction, 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, // 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!) // 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. // they are forced to choose the same width as all the other children.
if (alignItems == ASStackLayoutAlignItemsStretch && std::fabs(cross - childCrossMax) > 0.01) { 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, static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem> &items,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange) const ASSizeRange &sizeRange,
const CGSize size)
{ {
for (ASStackUnpositionedItem &item : items) { for (ASStackUnpositionedItem &item : items) {
const id<ASLayoutable> child = item.child; const id<ASLayoutable> child = item.child;
@@ -227,7 +227,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem
0, 0,
0, 0,
crossDimension(style.direction, sizeRange.min), 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, static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem> &items,
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange, const ASSizeRange &sizeRange,
const CGSize size,
const BOOL useOptimizedFlexing) const BOOL useOptimizedFlexing)
{ {
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style); const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
@@ -258,7 +260,7 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
if (flexibleChildren == 0) { 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 optimized flexing was used then we have to clean up the unsized children, and lay them out at zero size
if (useOptimizedFlexing) { if (useOptimizedFlexing) {
layoutFlexibleChildrenAtZeroSize(items, style, sizeRange); layoutFlexibleChildrenAtZeroSize(items, style, sizeRange, size);
} }
return; return;
} }
@@ -280,7 +282,8 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
MAX(flexedStackSize, 0), MAX(flexedStackSize, 0),
MAX(flexedStackSize, 0), MAX(flexedStackSize, 0),
crossDimension(style.direction, sizeRange.min), crossDimension(style.direction, sizeRange.min),
crossDimension(style.direction, sizeRange.max)); crossDimension(style.direction, sizeRange.max),
size);
isFirstFlex = NO; isFirstFlex = NO;
} }
} }
@@ -298,23 +301,19 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
{ {
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
return AS::map(children, [&](id<ASLayoutable> child) -> ASStackUnpositionedItem { 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)) { if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
return { child, [ASLayout layoutWithLayoutableObject:child constrainedSizeRange:sizeRange size:{0, 0}] }; return { child, [ASLayout layoutWithLayoutable:child size:{0, 0}] };
} else { } else {
return { return {
child, child,
crossChildLayout(child, crossChildLayout(child,
style, style,
isUnconstrainedFlexBasis ? 0 : exactStackDimension, ASDimensionResolve(child.flexBasis, stackDimension(style.direction, size), 0),
isUnconstrainedFlexBasis ? INFINITY : exactStackDimension, ASDimensionResolve(child.flexBasis, stackDimension(style.direction, size), INFINITY),
minCrossDimension, minCrossDimension,
maxCrossDimension) maxCrossDimension,
size)
}; };
} }
}); });
@@ -324,9 +323,11 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
const ASStackLayoutSpecStyle &style, const ASStackLayoutSpecStyle &style,
const ASSizeRange &sizeRange) 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 = { const CGSize size = {
sizeRange.max.width, (sizeRange.min.width == sizeRange.max.width) ? sizeRange.min.width : ASLayoutableParentDimensionUndefined,
sizeRange.max.height (sizeRange.min.height == sizeRange.max.height) ? sizeRange.min.height : ASLayoutableParentDimensionUndefined,
}; };
// We may be able to avoid some redundant layout passes // We may be able to avoid some redundant layout passes
@@ -341,8 +342,8 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
size, size,
optimizedFlexing); optimizedFlexing);
flexChildrenAlongStackDimension(items, style, sizeRange, optimizedFlexing); flexChildrenAlongStackDimension(items, style, sizeRange, size, optimizedFlexing);
stretchChildrenAlongCrossDimension(items, style); stretchChildrenAlongCrossDimension(items, style, size);
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style); const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
return {items, stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)}; return {items, stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)};

View File

@@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
/** /**
@abstract Creates the stack of TextKit components. @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 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. @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. @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. @abstract Creates the stack of TextKit components.
@param textStorage The NSTextStorage to use. @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. @param layoutManager The NSLayoutManager to use.
@return An `ASTextKitComponents` containing the created components. The text view component will be nil. @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. @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. // 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`. // Otherwise, we create a temporary stack to size for `constrainedWidth`.
if (CGRectGetWidth(components.textView.bounds) != 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:). // 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) { if (_sizingTextContainer == nil) {
// make this text container unbounded in height so that the layout manager will compute the total // 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. // 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; _sizingTextContainer.lineFragmentPadding = 0;
// use 0 regardless of what is in the attributes so that we get an accurate line count // 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]; NSRange longestWordRange = [str rangeOfString:longestWordNeedingResize];
NSMutableAttributedString *attrString = [textStorage attributedSubstringFromRange:longestWordRange].mutableCopy; 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 // check if the longest word is larger than our constrained width
if (longestWordSize.width > _constrainedSize.width) { if (longestWordSize.width > _constrainedSize.width) {

View File

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

View File

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

View File

@@ -45,10 +45,16 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions
{ {
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]); ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
foregroundNode.staticSize = {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 [self testLayoutSpec:layoutSpec
sizeRange:kSize sizeRange:kSize
@@ -83,11 +89,23 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce
- (void)testMinimumSizeRangeIsGivenToChildWhenNotCentering - (void)testMinimumSizeRangeIsGivenToChildWhenNotCentering
{ {
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10));
foregroundNode.staticSize = {10, 10};
foregroundNode.flexGrow = YES; 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]; [self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil];
} }

View File

@@ -77,7 +77,7 @@
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack1, stack2, node5]]; return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack1, stack2, node5]];
}; };
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)]; [node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node5); XCTAssertEqual(node.subnodes[0], node5);
XCTAssertEqual(node.subnodes[1], node1); XCTAssertEqual(node.subnodes[1], node1);
XCTAssertEqual(node.subnodes[2], node2); 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[0], node1);
XCTAssertEqual(node.subnodes[1], node2); XCTAssertEqual(node.subnodes[1], node2);
node.layoutState = @2; node.layoutState = @2;
[node invalidateCalculatedLayout]; [node invalidateCalculatedLayout];
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)]; [node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
XCTAssertEqual(node.subnodes[0], node1); XCTAssertEqual(node.subnodes[0], node1);
XCTAssertEqual(node.subnodes[1], node3); XCTAssertEqual(node.subnodes[1], node3);
@@ -157,12 +157,12 @@
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 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); XCTAssertEqual(node.subnodes[0], node1);
node.layoutState = @2; node.layoutState = @2;
[node invalidateCalculatedLayout]; [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 back to the main thread to let the insertion / deletion of subnodes happening
dispatch_async(dispatch_get_main_queue(), ^{ 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"]; 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); XCTAssertEqual(node.subnodes[0], node1);
node.layoutState = @2; node.layoutState = @2;

View File

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

View File

@@ -28,7 +28,7 @@
node.layoutSpecBlock = ^(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { node.layoutSpecBlock = ^(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5, 5, 5, 5) child:subnode]; 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); ASSnapshotVerifyNode(node, nil);
} }

View File

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

View File

@@ -11,6 +11,7 @@
// //
#import <XCTest/XCTest.h> #import <XCTest/XCTest.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASEditableTextNode.h> #import <AsyncDisplayKit/ASEditableTextNode.h>
static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta) 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."); 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 - (void)testCalculatedSizeIsGreaterThanOrEqualToConstrainedSize
{ {
for (NSInteger i = 10; i < 500; i += 50) { for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i); 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.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); 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) { for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i); CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_editableTextNode measure:constrainedSize]; CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_editableTextNode measure:calculatedSize]; 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)); 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) { for (CGFloat i = 10; i < 500; i *= 1.3) {
CGSize constrainedSize = CGSizeMake(i, i); CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_editableTextNode measure:constrainedSize]; CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_editableTextNode measure:calculatedSize]; 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)); 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 - (UIImage *)testImage
{ {
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"logo-square" NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"logo-square"
ofType:@"png" inDirectory:@"TestResources"]; ofType:@"png"
inDirectory:@"TestResources"];
return [UIImage imageWithContentsOfFile:path]; return [UIImage imageWithContentsOfFile:path];
} }
@@ -29,30 +30,32 @@
// trivial test case to ensure ASSnapshotTestCase works // trivial test case to ensure ASSnapshotTestCase works
ASImageNode *imageNode = [[ASImageNode alloc] init]; ASImageNode *imageNode = [[ASImageNode alloc] init];
imageNode.image = [self testImage]; imageNode.image = [self testImage];
[imageNode measure:CGSizeMake(100, 100)]; [imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
ASSnapshotVerifyNode(imageNode, nil); ASSnapshotVerifyNode(imageNode, nil);
} }
- (void)testForcedScaling - (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.image = [self testImage];
imageNode.forcedSize = CGSizeMake(100, 100);
// Snapshot testing requires that node is formally laid out. // Snapshot testing requires that node is formally laid out.
imageNode.preferredFrameSize = CGSizeMake(100, 100); [imageNode setSizeWithCGSize:forcedImageSize];
[imageNode measure:CGSizeMake(100, 100)]; [imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, forcedImageSize)];
ASSnapshotVerifyNode(imageNode, @"first"); ASSnapshotVerifyNode(imageNode, @"first");
imageNode.preferredFrameSize = CGSizeMake(200, 200); [imageNode setSizeWithCGSize:CGSizeMake(200, 200)];
[imageNode measure:CGSizeMake(200, 200)]; [imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(200, 200))];
ASSnapshotVerifyNode(imageNode, @"second"); 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 @end

View File

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

View File

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

View File

@@ -39,7 +39,7 @@
node.layoutSpecUnderTest = layoutSpec; node.layoutSpecUnderTest = layoutSpec;
[node measureWithSizeRange:sizeRange]; [node layoutThatFits:sizeRange];
ASSnapshotVerifyNode(node, identifier); ASSnapshotVerifyNode(node, identifier);
} }
@@ -60,12 +60,3 @@
} }
@end @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 - (void)testOverlay
{ {
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor]); ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor], {20, 20});
foregroundNode.staticSize = {20, 20};
ASLayoutSpec *layoutSpec = ASLayoutSpec *layoutSpec =
[ASOverlayLayoutSpec [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 - (void)testRatioLayoutSpecWithRatio:(CGFloat)ratio childSize:(CGSize)childSize identifier:(NSString *)identifier
{ {
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]); ASDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], childSize);
subnode.staticSize = childSize;
ASLayoutSpec *layoutSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:subnode]; ASLayoutSpec *layoutSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:subnode];

View File

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

View File

@@ -23,26 +23,31 @@
#pragma mark - Utility methods #pragma mark - Utility methods
static NSArray *defaultSubnodes() static NSArray<ASDisplayNode *> *defaultSubnodes()
{ {
return defaultSubnodesWithSameSize(CGSizeZero, NO); return defaultSubnodesWithSameSize(CGSizeZero, NO);
} }
static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) static NSArray<ASDisplayNode *> *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
NSArray *subnodes = @[ NSArray<ASDisplayNode *> *subnodes = @[
ASDisplayNodeWithBackgroundColor([UIColor redColor]), ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize),
ASDisplayNodeWithBackgroundColor([UIColor blueColor]), ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize),
ASDisplayNodeWithBackgroundColor([UIColor greenColor]) ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize)
]; ];
for (ASStaticSizeDisplayNode *subnode in subnodes) { for (ASDisplayNode *subnode in subnodes) {
subnode.staticSize = subnodeSize;
subnode.flexGrow = flex; subnode.flexGrow = flex;
subnode.flexShrink = flex; subnode.flexShrink = flex;
} }
return subnodes; return subnodes;
} }
static void setCGSizeToNode(CGSize size, ASDisplayNode *node)
{
node.width = ASDimensionMakeWithPoints(size.width);
node.height = ASDimensionMakeWithPoints(size.height);
}
- (void)testStackLayoutSpecWithJustify:(ASStackLayoutJustifyContent)justify - (void)testStackLayoutSpecWithJustify:(ASStackLayoutJustifyContent)justify
flex:(BOOL)flex flex:(BOOL)flex
sizeRange:(ASSizeRange)sizeRange sizeRange:(ASSizeRange)sizeRange
@@ -53,7 +58,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = justify .justifyContent = justify
}; };
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, flex); NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, flex);
[self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier]; [self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier];
} }
@@ -63,13 +68,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment
identifier:(NSString *)identifier identifier:(NSString *)identifier
{ {
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init]; ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init];
stackLayoutSpec.direction = direction; stackLayoutSpec.direction = direction;
stackLayoutSpec.children = subnodes; stackLayoutSpec.children = subnodes;
[stackLayoutSpec setHorizontalAlignment:horizontalAlignment]; stackLayoutSpec.horizontalAlignment = horizontalAlignment;
[stackLayoutSpec setVerticalAlignment:verticalAlignment]; stackLayoutSpec.verticalAlignment = verticalAlignment;
CGSize exactSize = CGSizeMake(200, 200); CGSize exactSize = CGSizeMake(200, 200);
static ASSizeRange kSize = ASSizeRangeMake(exactSize, exactSize); static ASSizeRange kSize = ASSizeRangeMake(exactSize, exactSize);
@@ -90,11 +95,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
subnodes:(NSArray *)subnodes subnodes:(NSArray *)subnodes
identifier:(NSString *)identifier identifier:(NSString *)identifier
{ {
ASStackLayoutSpec *stackLayoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction ASStackLayoutSpec *stackLayoutSpec =
spacing:style.spacing [ASStackLayoutSpec
justifyContent:style.justifyContent stackLayoutSpecWithDirection:style.direction
alignItems:style.alignItems spacing:style.spacing
children:children]; justifyContent:style.justifyContent
alignItems:style.alignItems
children:children];
[self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; [self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
} }
@@ -104,7 +112,6 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
identifier:(NSString *)identifier identifier:(NSString *)identifier
{ {
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]); ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]);
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:stackLayoutSpec background:backgroundNode]; ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:stackLayoutSpec background:backgroundNode];
NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode]; NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode];
@@ -145,8 +152,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
((ASDisplayNode *)subnodes[1]).flexShrink = YES; subnodes[1].flexShrink = YES;
// Width is 75px--that's less than the sum of the widths of the children, which is 100px. // 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}}; static ASSizeRange kSize = {{75, 0}, {75, 150}};
@@ -157,8 +164,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, YES); NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, YES);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; setCGSizeToNode({150, 150}, subnodes[1]);
// width 300px; height 0-150px. // width 300px; height 0-150px.
static ASSizeRange kUnderflowSize = {{300, 0}, {300, 150}}; static ASSizeRange kUnderflowSize = {{300, 0}, {300, 150}};
@@ -173,10 +180,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical};
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; setCGSizeToNode({100, 50}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 50}; setCGSizeToNode({150, 50}, subnodes[2]);
// width 0-300px; height 300px // width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@@ -194,10 +201,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.spacing = 10 .spacing = 10
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50}; setCGSizeToNode({100, 50}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 50}; setCGSizeToNode({150, 50}, subnodes[2]);
// width 0-300px; height 300px // width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@@ -212,12 +219,16 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
ASLayoutSpec *layoutSpec = ASLayoutSpec *layoutSpec =
[ASInsetLayoutSpec [ASInsetLayoutSpec
insetLayoutSpecWithInsets:{10, 10, 10 ,10} insetLayoutSpecWithInsets:{10, 10, 10, 10}
child: child:
[ASBackgroundLayoutSpec [ASBackgroundLayoutSpec
backgroundLayoutSpecWithChild: backgroundLayoutSpecWithChild:
[ASStackLayoutSpec [ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:10 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[]] stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:10
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:@[]]
background:backgroundNode]]; background:backgroundNode]];
// width 300px; height 0-300px // width 300px; height 0-300px
@@ -231,28 +242,28 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
static ASSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}}; static ASSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}};
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical};
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10; subnodes[1].spacingBefore = 10;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20; subnodes[2].spacingBefore = 20;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"]; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"];
// Reset above spacing values // Reset above spacing values
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0; subnodes[1].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0; subnodes[2].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10; subnodes[1].spacingAfter = 10;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20; subnodes[2].spacingAfter = 20;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"]; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"];
// Reset above spacing values // Reset above spacing values
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0; subnodes[1].spacingAfter = 0;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0; subnodes[2].spacingAfter = 0;
style.spacing = 10; style.spacing = 10;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10; subnodes[1].spacingBefore = -10;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10; subnodes[1].spacingAfter = -10;
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"]; [self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"];
} }
@@ -263,14 +274,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = ASStackLayoutJustifyContentCenter .justifyContent = ASStackLayoutJustifyContentCenter
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; subnodes[0].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; subnodes[1].spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; subnodes[2].spacingBefore = 30;
// width 0-300px; height 300px // width 0-300px; height 300px
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}}; static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
@@ -284,8 +295,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = ASStackLayoutJustifyContentSpaceBetween .justifyContent = ASStackLayoutJustifyContentSpaceBetween
}; };
ASStaticSizeDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
child.staticSize = {50, 50};
// width 300px; height 0-INF // width 300px; height 0-INF
static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}}; static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}};
@@ -299,8 +309,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.justifyContent = ASStackLayoutJustifyContentSpaceAround .justifyContent = ASStackLayoutJustifyContentSpaceAround
}; };
ASStaticSizeDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
child.staticSize = {50, 50};
// width 300px; height 0-INF // width 300px; height 0-INF
static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}}; static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}};
@@ -325,12 +334,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
ASStaticSizeDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); ASDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
ASStaticSizeDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
subnode2.staticSize = {50, 50};
ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1]; ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1];
child1.flexBasis = ASRelativeDimensionMakeWithFraction(1); child1.flexBasis = ASDimensionMakeWithFraction(1);
child1.flexGrow = YES; child1.flexGrow = YES;
child1.flexShrink = YES; child1.flexShrink = YES;
@@ -345,15 +353,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsCenter .alignItems = ASStackLayoutAlignItemsCenter
}; };
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100});
subnode1.staticSize = {100, 100};
subnode1.flexShrink = YES; subnode1.flexShrink = YES;
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50});
subnode2.staticSize = {50, 50};
subnode2.flexShrink = YES; subnode2.flexShrink = YES;
NSArray *subnodes = @[subnode1, subnode2]; NSArray<ASDisplayNode *> *subnodes = @[subnode1, subnode2];
static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}}; static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}};
[self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil]; [self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil];
} }
@@ -362,14 +368,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100});
subnode1.staticSize = {100, 100};
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]); ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50});
subnode2.staticSize = {50, 50};
subnode2.alignSelf = ASStackLayoutAlignSelfCenter; subnode2.alignSelf = ASStackLayoutAlignSelfCenter;
NSArray *subnodes = @[subnode1, subnode2]; NSArray<ASDisplayNode *> *subnodes = @[subnode1, subnode2];
static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}}; static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
[self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil]; [self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil];
} }
@@ -382,14 +386,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsStart .alignItems = ASStackLayoutAlignItemsStart
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; subnodes[0].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; subnodes[1].spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; subnodes[2].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@@ -403,14 +407,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsEnd .alignItems = ASStackLayoutAlignItemsEnd
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; subnodes[0].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; subnodes[1].spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; subnodes[2].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@@ -424,14 +428,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsCenter .alignItems = ASStackLayoutAlignItemsCenter
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; subnodes[0].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; subnodes[1].spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; subnodes[2].spacingBefore = 30;
static ASSizeRange kExactSize = {{300, 300}, {300, 300}}; static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil]; [self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
@@ -445,14 +449,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsStretch .alignItems = ASStackLayoutAlignItemsStretch
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; subnodes[0].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; subnodes[1].spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; subnodes[2].spacingBefore = 30;
static ASSizeRange kVariableSize = {{200, 200}, {300, 300}}; static ASSizeRange kVariableSize = {{200, 200}, {300, 300}};
// all children should be 200px wide // all children should be 200px wide
@@ -467,14 +471,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
.alignItems = ASStackLayoutAlignItemsStretch .alignItems = ASStackLayoutAlignItemsStretch
}; };
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70}; setCGSizeToNode({100, 70}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90}; setCGSizeToNode({150, 90}, subnodes[2]);
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0; subnodes[0].spacingBefore = 0;
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20; subnodes[1].spacingBefore = 20;
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30; subnodes[2].spacingBefore = 30;
static ASSizeRange kVariableSize = {{50, 50}, {300, 300}}; static ASSizeRange kVariableSize = {{50, 50}, {300, 300}};
// all children should be 150px wide // all children should be 150px wide
@@ -491,12 +495,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; setCGSizeToNode({150, 150}, subnodes[1]);
for (ASStaticSizeDisplayNode *subnode in subnodes) { for (ASDisplayNode *subnode in subnodes) {
subnode.flexGrow = YES; subnode.flexGrow = YES;
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10); subnode.flexBasis = ASDimensionMakeWithPoints(10);
} }
// width 300px; height 0-150px. // width 300px; height 0-150px.
@@ -512,15 +516,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO); NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
for (ASDisplayNode *subnode in subnodes) {
for (ASStaticSizeDisplayNode *subnode in subnodes) {
subnode.flexGrow = YES; subnode.flexGrow = YES;
} }
// This should override the intrinsic size of 50pts and instead compute to 50% = 100pts. // 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. // 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}}; static ASSizeRange kSize = {{200, 0}, {200, INFINITY}};
[self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil]; [self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil];
@@ -530,13 +533,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
NSArray *subnodes = defaultSubnodes(); NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50}; setCGSizeToNode({50, 50}, subnodes[0]);
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150}; setCGSizeToNode({150, 150}, subnodes[1]);
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50}; setCGSizeToNode({150, 50}, subnodes[2]);
for (ASStaticSizeDisplayNode *subnode in subnodes) { for (ASDisplayNode *subnode in subnodes) {
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20); subnode.flexBasis = ASDimensionMakeWithPoints(20);
} }
static ASSizeRange kSize = {{300, 0}, {300, 150}}; static ASSizeRange kSize = {{300, 0}, {300, 150}};
@@ -545,13 +548,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
- (void)testCrossAxisStretchingOccursAfterStackAxisFlexing - (void)testCrossAxisStretchingOccursAfterStackAxisFlexing
{ {
NSArray *subnodes = @[ NSArray<ASDisplayNode *> *subnodes = @[
ASDisplayNodeWithBackgroundColor([UIColor greenColor]), ASDisplayNodeWithBackgroundColor([UIColor greenColor]),
ASDisplayNodeWithBackgroundColor([UIColor blueColor]), ASDisplayNodeWithBackgroundColor([UIColor blueColor], {10, 0}),
ASDisplayNodeWithBackgroundColor([UIColor redColor]) ASDisplayNodeWithBackgroundColor([UIColor redColor], {3000, 3000})
]; ];
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {10, 0};
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000};
ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]]; ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]];
child2.flexGrow = YES; child2.flexGrow = YES;
@@ -565,7 +566,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
[ASInsetLayoutSpec [ASInsetLayoutSpec
insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10)
child: 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]]; background:subnodes[0]];
@@ -576,17 +582,16 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
- (void)testViolationIsDistributedEquallyAmongFlexibleChildren - (void)testViolationIsDistributedEquallyAmongFlexibleChildren
{ {
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal}; 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}; subnodes[0].flexShrink = YES;
((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES; subnodes[1].flexShrink = NO;
subnodes[2].flexShrink = YES;
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO;
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50};
((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES;
// A width of 400px results in a violation of 200px. This is distributed equally among each flexible child, // 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. // 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 - (void)testSizingBehaviour
{ {
[self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(FLT_MAX, FLT_MAX)) [self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(INFINITY, INFINITY))
identifier:@"underflowChildren"]; identifier:@"underflowChildren"];
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(50, 100)) [self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(50, 100))
identifier:@"overflowChildren"]; identifier:@"overflowChildren"];
// Expect the spec to wrap its content because children sizes are between constrained size // 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"]; identifier:@"wrappedChildren"];
} }
- (void)testChildrenMeasuredWithAutoMaxSize - (void)testChildrenMeasuredWithAutoMaxSize
{ {
ASStaticSizeDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]); ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor], (CGSize){50, 50});
firstChild.layoutPosition = CGPointMake(0, 0); 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.layoutPosition = CGPointMake(10, 60);
secondChild.staticSize = CGSizeMake(100, 100);
ASSizeRange sizeRange = ASSizeRangeMake(CGSizeMake(10, 10), CGSizeMake(110, 160)); ASSizeRange sizeRange = ASSizeRangeMake(CGSizeMake(10, 10), CGSizeMake(110, 160));
[self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:nil]; [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 - (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.layoutPosition = CGPointMake(0, 0);
firstChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor], (CGSize){100, 100});
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
secondChild.layoutPosition = CGPointMake(0, 50); secondChild.layoutPosition = CGPointMake(0, 50);
secondChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(100, 100));
[self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:identifier]; [self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:identifier];
} }
@@ -73,9 +61,12 @@
NSMutableArray *subnodes = [NSMutableArray arrayWithArray:children]; NSMutableArray *subnodes = [NSMutableArray arrayWithArray:children];
[subnodes insertObject:backgroundNode atIndex:0]; [subnodes insertObject:backgroundNode atIndex:0];
ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:children]; ASLayoutSpec *layoutSpec =
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:staticLayoutSpec [ASBackgroundLayoutSpec
background:backgroundNode]; backgroundLayoutSpecWithChild:
[ASStaticLayoutSpec
staticLayoutSpecWithChildren:children]
background:backgroundNode];
[self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier]; [self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
} }

View File

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

View File

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

View File

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

View File

@@ -12,6 +12,7 @@
#import <OCMock/OCMock.h> #import <OCMock/OCMock.h>
#import <AsyncDisplayKit/ASLayout.h>
#import <AsyncDisplayKit/ASTextNode.h> #import <AsyncDisplayKit/ASTextNode.h>
#import <XCTest/XCTest.h> #import <XCTest/XCTest.h>
@@ -111,7 +112,7 @@
{ {
for (NSInteger i = 10; i < 500; i += 50) { for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i); 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.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); 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) { for (NSInteger i = 10; i < 500; i += 50) {
CGSize constrainedSize = CGSizeMake(i, i); CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_textNode measure:constrainedSize]; CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_textNode measure:constrainedSize]; 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)); 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) { for (CGFloat i = 10; i < 500; i *= 1.3) {
CGSize constrainedSize = CGSizeMake(i, i); CGSize constrainedSize = CGSizeMake(i, i);
CGSize calculatedSize = [_textNode measure:constrainedSize]; CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
CGSize recalculatedSize = [_textNode measure:constrainedSize]; 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)); 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; _textNode.placeholderEnabled = YES;
XCTAssertNoThrow([_textNode measure:CGSizeZero], @"Measure with zero size 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 measure:CGSizeMake(0, 100)], @"Measure with zero width 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 measure:CGSizeMake(100, 0)], @"Measure with zero height 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 - (void)testAccessibility
@@ -170,7 +171,7 @@
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new]; ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
_textNode.delegate = delegate; _textNode.delegate = delegate;
[_textNode measure:CGSizeMake(100, 100)]; [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
NSRange returnedLinkRange; NSRange returnedLinkRange;
NSString *returnedAttributeName; NSString *returnedAttributeName;
NSString *returnedLinkAttributeValue = [_textNode linkAttributeValueAtPoint:CGPointMake(3, 3) attributeName:&returnedAttributeName range:&returnedLinkRange]; NSString *returnedLinkAttributeValue = [_textNode linkAttributeValueAtPoint:CGPointMake(3, 3) attributeName:&returnedAttributeName range:&returnedLinkRange];
@@ -192,7 +193,7 @@
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new]; ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
_textNode.delegate = delegate; _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 returnedLinkRange = NSMakeRange(NSNotFound, 0);
NSRange expectedRange = NSMakeRange(NSNotFound, 0); NSRange expectedRange = NSMakeRange(NSNotFound, 0);
NSString *returnedAttributeName; NSString *returnedAttributeName;
@@ -218,9 +219,9 @@
- (void)testAddingExclusionPathsShouldInvalidateAndIncreaseTheSize - (void)testAddingExclusionPathsShouldInvalidateAndIncreaseTheSize
{ {
CGSize constrainedSize = CGSizeMake(100, CGFLOAT_MAX); 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)]]; _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"); 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) #define AS_UNAVAILABLE(message)
#endif #endif
#endif #endif
#define ASOVERLOADABLE __attribute__((overloadable))

View File

@@ -192,7 +192,7 @@
{ {
[super viewDidLayoutSubviews]; [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); self.transitionNode.frame = CGRectMake(0, 20, size.width, size.height);
} }

View File

@@ -65,7 +65,8 @@
[self addSubnode:_likeButtonNode]; [self addSubnode:_likeButtonNode];
_muteButtonNode = [[ASButtonNode alloc] init]; _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]; [_muteButtonNode addTarget:self action:@selector(didTapMuteButton) forControlEvents:ASControlNodeEventTouchUpInside];
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url loadAssetWhenNodeBecomesVisible:YES]; _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; CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;
_videoPlayerNode.preferredFrameSize = CGSizeMake(fullWidth, fullWidth * 9 / 16);
_avatarNode.preferredFrameSize = CGSizeMake(AVATAR_IMAGE_HEIGHT, AVATAR_IMAGE_HEIGHT); _videoPlayerNode.width = ASDimensionMakeWithPoints(fullWidth);
_likeButtonNode.preferredFrameSize = CGSizeMake(50.0, 26.0); _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]; ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
headerStack.spacing = HORIZONTAL_BUFFER; headerStack.spacing = HORIZONTAL_BUFFER;
@@ -104,15 +111,15 @@
ASStackLayoutSpec *bottomControlsStack = [ASStackLayoutSpec horizontalStackLayoutSpec]; ASStackLayoutSpec *bottomControlsStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
bottomControlsStack.spacing = HORIZONTAL_BUFFER; bottomControlsStack.spacing = HORIZONTAL_BUFFER;
bottomControlsStack.alignItems = ASStackLayoutAlignItemsCenter; 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]; ASInsetLayoutSpec *bottomControlsInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:bottomControlsInsets child:bottomControlsStack];
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec]; ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.alignItems = ASStackLayoutAlignItemsStretch; verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
[verticalStack setChildren:@[ headerInset, _videoPlayerNode, bottomControlsInset ]]; verticalStack.children = @[headerInset, _videoPlayerNode, bottomControlsInset];
return verticalStack; return verticalStack;
} }
@@ -183,7 +190,10 @@
if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) { if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
ASDisplayNode *scrubber = 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]; NSArray *controlBarControls = [self controlsForControlBar:controls];

View File

@@ -126,7 +126,9 @@
// header stack // 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 _photoTimeIntervalSincePostLabel.spacingBefore = HORIZONTAL_BUFFER; // to remove double spaces around spacer
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size? ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size?
@@ -155,7 +157,10 @@
// vertical stack // vertical stack
CGFloat cellWidth = constrainedSize.max.width; 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]; ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -42,8 +42,10 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{ {
_node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max); // 100% of container
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_node]]; _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; BOOL hasDescription = self.descriptionNode.attributedText.length > 0;
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec]; ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackLayoutSpec.alignItems = ASStackLayoutAlignItemsStart;
verticalStackLayoutSpec.spacing = 5.0; verticalStackLayoutSpec.spacing = 5.0;
verticalStackLayoutSpec.children = hasDescription ? @[self.titleNode, self.descriptionNode] : @[self.titleNode]; verticalStackLayoutSpec.children = hasDescription ? @[self.titleNode, self.descriptionNode] : @[self.titleNode];
@@ -203,8 +204,11 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASImageNode #pragma mark ASImageNode
ASImageNode *imageNode = [ASImageNode new]; ASImageNode *imageNode = [ASImageNode new];
imageNode.image = [UIImage imageNamed:@"image"]; imageNode.image = [UIImage imageNamed:@"image.jpg"];
imageNode.preferredFrameSize = CGSizeMake(imageNode.image.size.width / 7, imageNode.image.size.height / 7);
CGSize imageNetworkImageNodeSize = (CGSize){imageNode.image.size.width / 7, imageNode.image.size.height / 7};
[imageNode setSizeWithCGSize:imageNetworkImageNodeSize];
parentNode = [self centeringParentNodeWithChild:imageNode]; parentNode = [self centeringParentNodeWithChild:imageNode];
parentNode.entryTitle = @"ASImageNode"; parentNode.entryTitle = @"ASImageNode";
@@ -214,7 +218,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASNetworkImageNode #pragma mark ASNetworkImageNode
ASNetworkImageNode *networkImageNode = [ASNetworkImageNode new]; ASNetworkImageNode *networkImageNode = [ASNetworkImageNode new];
networkImageNode.URL = [NSURL URLWithString:@"http://i.imgur.com/FjOR9kX.jpg"]; 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 = [self centeringParentNodeWithChild:networkImageNode];
parentNode.entryTitle = @"ASNetworkImageNode"; parentNode.entryTitle = @"ASNetworkImageNode";
@@ -223,7 +227,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASMapNode #pragma mark ASMapNode
ASMapNode *mapNode = [ASMapNode new]; ASMapNode *mapNode = [ASMapNode new];
mapNode.preferredFrameSize = CGSizeMake(300.0, 300.0); [mapNode setSizeWithCGSize:(CGSize){300.0, 300.0}];
// San Francisco // San Francisco
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194); CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194);
@@ -236,7 +240,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark ASVideoNode #pragma mark ASVideoNode
ASVideoNode *videoNode = [ASVideoNode new]; 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"]]; AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://www.w3schools.com/html/mov_bbb.mp4"]];
videoNode.asset = asset; videoNode.asset = asset;
@@ -250,7 +254,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
UIImage *scrollNodeImage = [UIImage imageNamed:@"image"]; UIImage *scrollNodeImage = [UIImage imageNamed:@"image"];
ASScrollNode *scrollNode = [ASScrollNode new]; ASScrollNode *scrollNode = [ASScrollNode new];
scrollNode.preferredFrameSize = CGSizeMake(300.0, 400.0); [scrollNode setSizeWithCGSize:(CGSize){300.0, 400.0}];
UIScrollView *scrollNodeView = scrollNode.view; UIScrollView *scrollNodeView = scrollNode.view;
[scrollNodeView addSubview:[[UIImageView alloc] initWithImage:scrollNodeImage]]; [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.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) { parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) {
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec]; ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
verticalStackLayoutSpec.alignItems = ASStackLayoutAlignItemsStart;
verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3]; verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3];
return verticalStackLayoutSpec; return verticalStackLayoutSpec;
}; };
@@ -401,17 +406,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
#pragma mark Horizontal ASStackLayoutSpec #pragma mark Horizontal ASStackLayoutSpec
childNode1 = [ASDisplayNode new]; childNode1 = [ASDisplayNode new];
childNode1.preferredFrameSize = CGSizeMake(10.0, 20); [childNode1 setSizeWithCGSize:(CGSize){10.0, 20.0}];
childNode1.flexGrow = YES; childNode1.flexGrow = YES;
childNode1.backgroundColor = [UIColor greenColor]; childNode1.backgroundColor = [UIColor greenColor];
childNode2 = [ASDisplayNode new]; childNode2 = [ASDisplayNode new];
childNode2.preferredFrameSize = CGSizeMake(10.0, 20.0); [childNode2 setSizeWithCGSize:(CGSize){10.0, 20.0}];
childNode2.alignSelf = ASStackLayoutAlignSelfStretch; childNode2.alignSelf = ASStackLayoutAlignSelfStretch;
childNode2.backgroundColor = [UIColor blueColor]; childNode2.backgroundColor = [UIColor blueColor];
childNode3 = [ASDisplayNode new]; childNode3 = [ASDisplayNode new];
childNode3.preferredFrameSize = CGSizeMake(10.0, 20.0); [childNode3 setSizeWithCGSize:(CGSize){10.0, 20.0}];
childNode3.backgroundColor = [UIColor yellowColor]; childNode3.backgroundColor = [UIColor yellowColor];
parentNode = [self parentNodeWithChild:childNode]; parentNode = [self parentNodeWithChild:childNode];
@@ -420,20 +425,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) { parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) {
// Create stack alyout spec to layout children // Create stack alyout spec to layout children
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec horizontalStackLayoutSpec]; ASStackLayoutSpec *horizontalStackSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3]; horizontalStackSpec.alignItems = ASStackLayoutAlignItemsStart;
verticalStackLayoutSpec.spacing = 5.0; // Spacing between children 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 // Layout the stack layout with 100% width and 100% height of the parent node
ASRelativeSizeRange sizeRange = ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimensionMakeWithFraction(1), horizontalStackSpec.height = ASDimensionMakeWithFraction(1.0);
ASRelativeDimensionMakeWithFraction(1)); horizontalStackSpec.width = ASDimensionMakeWithFraction(1.0);
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]];
// Add a bit of inset // 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:childNode1];
[parentNode addSubnode:childNode2]; [parentNode addSubnode:childNode2];
@@ -465,7 +467,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
- (OverviewDisplayNodeWithSizeBlock *)parentNodeWithChild:(ASDisplayNode *)child - (OverviewDisplayNodeWithSizeBlock *)parentNodeWithChild:(ASDisplayNode *)child
{ {
OverviewDisplayNodeWithSizeBlock *parentNode = [OverviewDisplayNodeWithSizeBlock new]; OverviewDisplayNodeWithSizeBlock *parentNode = [OverviewDisplayNodeWithSizeBlock new];
parentNode.preferredFrameSize = CGSizeMake(100, 100); [parentNode setSizeWithCGSize:(CGSize){100, 100}];
parentNode.backgroundColor = [UIColor redColor]; parentNode.backgroundColor = [UIColor redColor];
return parentNode; return parentNode;
} }
@@ -489,7 +491,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
- (ASDisplayNode *)childNode - (ASDisplayNode *)childNode
{ {
ASDisplayNode *childNode = [ASDisplayNode new]; ASDisplayNode *childNode = [ASDisplayNode new];
childNode.preferredFrameSize = CGSizeMake(50, 50); [childNode setSizeWithCGSize:(CGSize){50, 50}];
childNode.backgroundColor = [UIColor blueColor]; childNode.backgroundColor = [UIColor blueColor];
return childNode; return childNode;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -68,13 +68,11 @@
_tableView.frame = self.view.bounds; _tableView.frame = self.view.bounds;
} }
#pragma mark - #pragma mark - ASTableView.
#pragma mark ASTableView.
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath - (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
HorizontalScrollCellNode *node = [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)]; return [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)];
return node;
} }
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (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 // lorem ipsum text, plus some nice styling
_textNode = [[ASTextNode alloc] init]; _textNode = [[ASTextNode alloc] init];
_textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum] _textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum] attributes:[self textStyle]];
attributes:[self textStyle]];
[self addSubnode:_textNode]; [self addSubnode:_textNode];
// hairline cell separator // hairline cell separator
@@ -134,27 +133,36 @@ static const CGFloat kInnerPadding = 10.0f;
style.paragraphSpacing = 0.5 * font.lineHeight; style.paragraphSpacing = 0.5 * font.lineHeight;
style.hyphenationFactor = 1.0; style.hyphenationFactor = 1.0;
return @{ NSFontAttributeName: font, return @{
NSParagraphStyleAttributeName: style, NSFontAttributeName: font,
ASTextNodeWordKerningAttributeName : @.5}; NSParagraphStyleAttributeName: style,
ASTextNodeWordKerningAttributeName : @.5
};
} }
#if UseAutomaticLayout #if UseAutomaticLayout
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize - (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; _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]; // Add inset
stackSpec.direction = ASStackLayoutDirectionHorizontal; return [ASInsetLayoutSpec
stackSpec.spacing = kInnerPadding; insetLayoutSpecWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding)
[stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]]; child:stackLayoutSpec];
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding);
insetSpec.child = stackSpec;
return insetSpec;
} }
// With box model, you don't need to override this method, unless you want to add custom logic. // 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 - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
{ {
return [ASLayout layoutWithLayoutableObject:self return [ASLayout layoutWithLayoutable:self size:constrainedSize.max];
constrainedSizeRange:constrainedSize
size:constrainedSize.max];
} }
- (void)fetchData - (void)fetchData

View File

@@ -52,13 +52,19 @@
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize - (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 // Adjust size
ASRelativeSize min = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(0.0)); mainStack.minWidth = ASDimensionMakeWithPoints(60.0);
ASRelativeSize max = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(40.0)); mainStack.maxHeight = ASDimensionMakeWithPoints(40.0);
mainStack.sizeRange = ASRelativeSizeRangeMake(min,max);
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[mainStack]]; return mainStack;
} }
@end @end

View File

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

View File

@@ -20,6 +20,7 @@
#import "TextStyles.h" #import "TextStyles.h"
#import "LikesNode.h" #import "LikesNode.h"
#import "CommentsNode.h" #import "CommentsNode.h"
#import "ASRelativeSize.h"
#define PostNodeDividerColor [UIColor lightGrayColor] #define PostNodeDividerColor [UIColor lightGrayColor]
@@ -136,7 +137,8 @@
// User pic // User pic
_avatarNode = [[ASNetworkImageNode alloc] init]; _avatarNode = [[ASNetworkImageNode alloc] init];
_avatarNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor(); _avatarNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
_avatarNode.preferredFrameSize = CGSizeMake(44, 44); _avatarNode.width = ASDimensionMakeWithPoints(44);
_avatarNode.height = ASDimensionMakeWithPoints(44);
_avatarNode.cornerRadius = 22.0; _avatarNode.cornerRadius = 22.0;
_avatarNode.URL = [NSURL URLWithString:_post.photo]; _avatarNode.URL = [NSURL URLWithString:_post.photo];
_avatarNode.imageModificationBlock = ^UIImage *(UIImage *image) { _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 // 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. // 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; // 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 // Horizontal stack for name, username, via icon and time
NSMutableArray *layoutSpecChildren = [@[_nameNode, _usernameNode, insetSpacer] mutableCopy]; NSMutableArray *layoutSpecChildren = [@[_nameNode, _usernameNode, insetSpacer] mutableCopy];
@@ -223,11 +228,23 @@
} }
[layoutSpecChildren addObject:_timeNode]; [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; nameStack.alignSelf = ASStackLayoutAlignSelfStretch;
// bottom controls horizontal stack // 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 // Add more gaps for control line
controlsStack.spacingAfter = 3.0; controlsStack.spacingAfter = 3.0;
@@ -237,26 +254,46 @@
[mainStackContent addObject:nameStack]; [mainStackContent addObject:nameStack];
[mainStackContent addObject:_postNode]; [mainStackContent addObject:_postNode];
if (![_post.media isEqualToString:@""]) {
CGFloat imageRatio = (_mediaNode.image != nil ? _mediaNode.image.size.height / _mediaNode.image.size.width : 0.5); if (![_post.media isEqualToString:@""]){
ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:_mediaNode];
imagePlace.spacingAfter = 3.0;
imagePlace.spacingBefore = 3.0;
[mainStackContent addObject:imagePlace];
// 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]; [mainStackContent addObject:controlsStack];
// Vertical spec of cell main content // Vertical spec of cell main content
ASStackLayoutSpec *contentSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:8.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:mainStackContent]; ASStackLayoutSpec *contentSpec =
contentSpec.alignItems = ASStackLayoutAlignSelfStretch; [ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:8.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:mainStackContent];
contentSpec.flexShrink = YES; contentSpec.flexShrink = YES;
// Horizontal spec for avatar // 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]; [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.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // SocialAppNode has its own separator self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // SocialAppNode has its own separator
self.tableView.asyncDataSource = self; self.tableView.asyncDataSource = self;

View File

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

View File

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

View File

@@ -127,14 +127,15 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate
/// (Pretend) fetches some new items and calls the /// (Pretend) fetches some new items and calls the
/// completion handler on the main thread. /// completion handler on the main thread.
private static func fetchDataWithCompletion(completion: (Int) -> Void) { 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()) { dispatch_after(time, dispatch_get_main_queue()) {
let resultCount = Int(arc4random_uniform(20)) let resultCount = Int(arc4random_uniform(20))
completion(resultCount) 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 { switch action {
case .BeginBatchFetch: case .BeginBatchFetch:
state.fetchingMore = true state.fetchingMore = true

View File

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

View File

@@ -71,17 +71,11 @@
_indexPathTextNode.attributedText = [[NSAttributedString alloc] initWithString:[indexPath description] attributes:nil]; _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 - (void)layout
{ {
_indexPathTextNode.frame = self.bounds;
[super layout]; [super layout];
_indexPathTextNode.frame = self.bounds;
} }
#if 0 #if 0

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