mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 14:20:20 +00:00
[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:
committed by
Adlai Holler
parent
2bfeb6de92
commit
8897614f0e
@@ -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 */,
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;\
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
58
AsyncDisplayKit/Private/ASDisplayNodeLayout.h
Normal file
58
AsyncDisplayKit/Private/ASDisplayNodeLayout.h
Normal 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();
|
||||||
|
};
|
||||||
34
AsyncDisplayKit/Private/ASDisplayNodeLayout.mm
Normal file
34
AsyncDisplayKit/Private/ASDisplayNodeLayout.mm
Normal 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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#import <CoreGraphics/CGBase.h>
|
|
||||||
|
|
||||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)};
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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:).
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,3 +151,5 @@
|
|||||||
#define AS_UNAVAILABLE(message)
|
#define AS_UNAVAILABLE(message)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ASOVERLOADABLE __attribute__((overloadable))
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
@@ -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]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user