mirror of
https://github.com/HackPlan/AsyncDisplayKit.git
synced 2026-03-26 10:04:32 +08: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 */; };
|
||||
69708BA71D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; };
|
||||
69708BA81D76386D005C3CF9 /* ASEqualityHashHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */; };
|
||||
6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; };
|
||||
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */; };
|
||||
695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */; };
|
||||
697B315A1CFE4B410049936F /* ASEditableTextNodeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */; };
|
||||
697C0DE41CF38F28001DE0D4 /* ASLayoutValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = 697C0DE11CF38F28001DE0D4 /* ASLayoutValidation.h */; };
|
||||
697C0DE51CF38F28001DE0D4 /* ASLayoutValidation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 697C0DE21CF38F28001DE0D4 /* ASLayoutValidation.mm */; };
|
||||
@@ -969,6 +972,8 @@
|
||||
696FCB301D6E46050093471E /* ASBackgroundLayoutSpecSnapshotTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASBackgroundLayoutSpecSnapshotTests.mm; sourceTree = "<group>"; };
|
||||
69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEqualityHashHelpers.h; path = TextKit/ASEqualityHashHelpers.h; sourceTree = "<group>"; };
|
||||
69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEqualityHashHelpers.mm; path = TextKit/ASEqualityHashHelpers.mm; sourceTree = "<group>"; };
|
||||
6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = "<group>"; };
|
||||
6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = "<group>"; };
|
||||
697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASEditableTextNodeTests.m; sourceTree = "<group>"; };
|
||||
697C0DE11CF38F28001DE0D4 /* ASLayoutValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutValidation.h; path = AsyncDisplayKit/Layout/ASLayoutValidation.h; sourceTree = "<group>"; };
|
||||
697C0DE21CF38F28001DE0D4 /* ASLayoutValidation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutValidation.mm; path = AsyncDisplayKit/Layout/ASLayoutValidation.mm; sourceTree = "<group>"; };
|
||||
@@ -1518,6 +1523,8 @@
|
||||
044285051BAA63FE00D16268 /* ASBatchFetching.h */,
|
||||
044285061BAA63FE00D16268 /* ASBatchFetching.m */,
|
||||
251B8EF61BBB3D690087C538 /* ASDataController+Subclasses.h */,
|
||||
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
|
||||
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
|
||||
AEB7B0181C5962EA00662EF4 /* ASDefaultPlayButton.h */,
|
||||
AEB7B0191C5962EA00662EF4 /* ASDefaultPlayButton.m */,
|
||||
058D0A08195D050800B7D73C /* ASDisplayNode+AsyncDisplay.mm */,
|
||||
@@ -1526,6 +1533,8 @@
|
||||
DE6EA3211C14000600183B10 /* ASDisplayNode+FrameworkPrivate.h */,
|
||||
058D0A0B195D050800B7D73C /* ASDisplayNode+UIViewBridge.mm */,
|
||||
058D0A0C195D050800B7D73C /* ASDisplayNodeInternal.h */,
|
||||
6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */,
|
||||
6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */,
|
||||
69E100691CA89CB600D88C1B /* ASEnvironmentInternal.h */,
|
||||
69E1006A1CA89CB600D88C1B /* ASEnvironmentInternal.mm */,
|
||||
68B8A4DB1CBD911D007E4543 /* ASImageNode+AnimatedImagePrivate.h */,
|
||||
@@ -1551,8 +1560,6 @@
|
||||
ACF6ED4A1B17847A00DA7C62 /* ASStackUnpositionedLayout.mm */,
|
||||
83A7D9581D44542100BF333E /* ASWeakMap.h */,
|
||||
83A7D9591D44542100BF333E /* ASWeakMap.m */,
|
||||
8B0768B11CE752EC002E1453 /* ASDefaultPlaybackButton.h */,
|
||||
8B0768B21CE752EC002E1453 /* ASDefaultPlaybackButton.m */,
|
||||
);
|
||||
path = Private;
|
||||
sourceTree = "<group>";
|
||||
@@ -1808,6 +1815,7 @@
|
||||
697C0DE41CF38F28001DE0D4 /* ASLayoutValidation.h in Headers */,
|
||||
B35062211B010EFD0018CF92 /* ASLayoutRangeType.h in Headers */,
|
||||
34EFC76A1B701CE600AD841F /* ASLayoutSpec.h in Headers */,
|
||||
695943401D70815300B0EE1F /* ASDisplayNodeLayout.h in Headers */,
|
||||
34EFC7791B701D3600AD841F /* ASLayoutSpecUtilities.h in Headers */,
|
||||
B350625C1B010F070018CF92 /* ASLog.h in Headers */,
|
||||
0442850E1BAA64EC00D16268 /* ASMultidimensionalArrayUtils.h in Headers */,
|
||||
@@ -2187,6 +2195,7 @@
|
||||
68355B311CB5799E001D4E68 /* ASImageNode+AnimatedImage.mm in Sources */,
|
||||
9CFFC6C01CCAC73C006A6476 /* ASViewController.mm in Sources */,
|
||||
055F1A3519ABD3E3004DAFF1 /* ASTableView.mm in Sources */,
|
||||
6959433E1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
|
||||
058D0A17195D050800B7D73C /* ASTextNode.mm in Sources */,
|
||||
257754AC1BEE44CD00737CA5 /* ASTextKitRenderer.mm in Sources */,
|
||||
8BDA5FC61CDBDDE1007D13B2 /* ASVideoPlayerNode.mm in Sources */,
|
||||
@@ -2368,6 +2377,7 @@
|
||||
DB78412E1C6BCE1600A9E2B4 /* _ASTransitionContext.m in Sources */,
|
||||
B350620B1B010EFD0018CF92 /* ASTableView.mm in Sources */,
|
||||
B350620E1B010EFD0018CF92 /* ASTextNode.mm in Sources */,
|
||||
6959433F1D70815300B0EE1F /* ASDisplayNodeLayout.mm in Sources */,
|
||||
68355B3E1CB57A60001D4E68 /* ASPINRemoteImageDownloader.m in Sources */,
|
||||
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */,
|
||||
254C6B871BF94F8A003EC431 /* ASTextKitEntityAttribute.m in Sources */,
|
||||
|
||||
@@ -491,10 +491,17 @@
|
||||
spec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:contentEdgeInsets child:spec];
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) {
|
||||
stack.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(self.preferredFrameSize);
|
||||
#if DEBUG
|
||||
NSLog(@"Using -[ASDisplayNde preferredFrameSize] is deprecated.");
|
||||
#endif
|
||||
stack.width = ASDimensionMake(ASDimensionUnitPoints, self.preferredFrameSize.width);
|
||||
stack.height = ASDimensionMake(ASDimensionUnitPoints, self.preferredFrameSize.height);
|
||||
spec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack]];
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
if (_backgroundImageNode.image) {
|
||||
spec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:spec background:_backgroundImageNode];
|
||||
|
||||
@@ -14,6 +14,38 @@
|
||||
|
||||
@interface ASDisplayNode (Deprecated)
|
||||
|
||||
/**
|
||||
* @abstract Asks the node to measure and return the size that best fits its subnodes.
|
||||
*
|
||||
* @param constrainedSize The maximum size the receiver should fit in.
|
||||
*
|
||||
* @return A new size that fits the receiver's subviews.
|
||||
*
|
||||
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
|
||||
* constraint and the result.
|
||||
*
|
||||
* @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
|
||||
* -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
|
||||
* be expensive if result is not cached.
|
||||
*
|
||||
* @see measureWithSizeRange:
|
||||
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
|
||||
*
|
||||
* @deprecated Deprecated in version 2.0: Use layoutThatFits: with a constrained size of (CGSizeZero, constrainedSize) and call size on the returned ASLayout
|
||||
*/
|
||||
- (CGSize)measure:(CGSize)constrainedSize ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* @abstract Calculate a layout based on given size range.
|
||||
*
|
||||
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
|
||||
*
|
||||
* @return An ASLayout instance defining the layout of the receiver and its children.
|
||||
*
|
||||
* @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout or ASCalculateLayout instead
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
/**
|
||||
* @abstract Called whenever the visiblity of the node changed.
|
||||
*
|
||||
|
||||
@@ -118,6 +118,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
|
||||
|
||||
/**
|
||||
* ASDisplayNode's implementation of -layoutThatFits:parentSize: calls this method to resolve the node's size
|
||||
* against parentSize, intersect it with constrainedSize, and call -calculateLayoutThatFits: with the result.
|
||||
*
|
||||
* In certain advanced cases, you may want to customize this logic. Overriding this method allows you to receive all
|
||||
* three parameters and do the computation yourself.
|
||||
*
|
||||
* @warning Overriding this method should be done VERY rarely.
|
||||
*/
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize;
|
||||
|
||||
/**
|
||||
* @abstract Return the calculated size.
|
||||
*
|
||||
@@ -130,7 +143,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*
|
||||
* @note Subclasses that override are committed to manual layout. Therefore, -layout: must be overriden to layout all subnodes or subviews.
|
||||
*
|
||||
* @note This method should not be called directly outside of ASDisplayNode; use -measure: or -calculatedLayout instead.
|
||||
* @note This method should not be called directly outside of ASDisplayNode; use -layoutThatFits: or layoutThatFits:parentSize: instead.
|
||||
*/
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize;
|
||||
|
||||
|
||||
@@ -248,26 +248,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/** @name Managing dimensions */
|
||||
|
||||
/**
|
||||
* @abstract Asks the node to measure and return the size that best fits its subnodes.
|
||||
*
|
||||
* @param constrainedSize The maximum size the receiver should fit in.
|
||||
*
|
||||
* @return A new size that fits the receiver's subviews.
|
||||
*
|
||||
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
|
||||
* constraint and the result.
|
||||
*
|
||||
* @warning Subclasses must not override this; it calls -measureWithSizeRange: with zero min size.
|
||||
* -measureWithSizeRange: caches results from -calculateLayoutThatFits:. Calling this method may
|
||||
* be expensive if result is not cached.
|
||||
*
|
||||
* @see measureWithSizeRange:
|
||||
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
|
||||
*/
|
||||
- (CGSize)measure:(CGSize)constrainedSize;
|
||||
|
||||
/**
|
||||
* @abstract Asks the node to measure a layout based on given size range.
|
||||
* @abstract Asks the node to return a layout based on given size range.
|
||||
*
|
||||
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
|
||||
*
|
||||
@@ -281,8 +262,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*
|
||||
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
|
||||
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;
|
||||
|
||||
/**
|
||||
* @abstract Provides a way to declare a block to provide an ASLayoutSpec without having to subclass ASDisplayNode and
|
||||
@@ -317,16 +297,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, readonly, assign) ASSizeRange constrainedSizeForCalculatedLayout;
|
||||
|
||||
/**
|
||||
* @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out
|
||||
* a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content
|
||||
* size. For example, this property could be set on an ASImageNode to display at a size different from the underlying
|
||||
* image size.
|
||||
*
|
||||
* @return The preferred frame size of this node
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) CGSize preferredFrameSize;
|
||||
|
||||
/** @name Managing the nodes hierarchy */
|
||||
|
||||
|
||||
@@ -625,7 +595,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* Convenience methods for debugging.
|
||||
*/
|
||||
|
||||
@interface ASDisplayNode (Debugging) <ASLayoutableAsciiArtProtocol>
|
||||
|
||||
/**
|
||||
@@ -838,6 +807,20 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (void)cancelLayoutTransition;
|
||||
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
/**
|
||||
* @abstract Provides a default intrinsic content size for calculateSizeThatFits:. This is useful when laying out
|
||||
* a node that either has no intrinsic content size or should be laid out at a different size than its intrinsic content
|
||||
* size. For example, this property could be set on an ASImageNode to display at a size different from the underlying
|
||||
* image size.
|
||||
*
|
||||
* @return The preferred frame size of this node
|
||||
*
|
||||
* @deprecated Deprecated in version 2.0: Use sizing properties instead: height, minHeight, maxHeight, width, minWidth, maxWidth
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) CGSize preferredFrameSize ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#import "ASEqualityHelpers.h"
|
||||
#import "ASRunLoopQueue.h"
|
||||
#import "ASEnvironmentInternal.h"
|
||||
#import "ASDimension.h"
|
||||
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASLayout.h"
|
||||
@@ -68,14 +69,19 @@ NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp = @"AS
|
||||
|
||||
@implementation ASDisplayNode
|
||||
|
||||
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
|
||||
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis,
|
||||
alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType;
|
||||
// Dynamic properties for ASLayoutables
|
||||
@dynamic layoutableType, size;
|
||||
// Dynamic properties for sizing
|
||||
@dynamic width, height, minWidth, maxWidth, minHeight, maxHeight;
|
||||
// Dynamic properties for stack spec
|
||||
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender;
|
||||
// Dynamic properties for static spec
|
||||
@dynamic layoutPosition;
|
||||
|
||||
@synthesize name = _name;
|
||||
@synthesize preferredFrameSize = _preferredFrameSize;
|
||||
@synthesize isFinalLayoutable = _isFinalLayoutable;
|
||||
@synthesize threadSafeBounds = _threadSafeBounds;
|
||||
@synthesize layoutSpecBlock = _layoutSpecBlock;
|
||||
|
||||
static BOOL suppressesInvalidCollectionUpdateExceptions = NO;
|
||||
|
||||
@@ -192,12 +198,16 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
if (self != [ASDisplayNode class]) {
|
||||
|
||||
// Subclasses should never override these
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", NSStringFromClass(self));
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", NSStringFromClass(self));
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure method", NSStringFromClass(self));
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange method", NSStringFromClass(self));
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", NSStringFromClass(self));
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", NSStringFromClass(self));
|
||||
NSString *classString = NSStringFromClass(self);
|
||||
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedSize)), @"Subclass %@ must not override calculatedSize method", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(calculatedLayout)), @"Subclass %@ must not override calculatedLayout method", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measure:)), @"Subclass %@ must not override measure: method", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead overwrite calculateLayoutThatFits:", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:)), @"Subclass %@ must not override layoutThatFits: method", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(layoutThatFits:parentSize:)), @"Subclass %@ must not override layoutThatFits:parentSize method", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearContents)), @"Subclass %@ must not override recursivelyClearContents method", classString);
|
||||
ASDisplayNodeAssert(!ASDisplayNodeSubclassOverridesSelector(self, @selector(recursivelyClearFetchedData)), @"Subclass %@ must not override recursivelyClearFetchedData method", classString);
|
||||
}
|
||||
|
||||
// Below we are pre-calculating values per-class and dynamically adding a method (_staticInitialize) to populate these values
|
||||
@@ -294,10 +304,13 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
[self _staticInitialize];
|
||||
_contentsScaleForDisplay = ASScreenScale();
|
||||
_displaySentinel = [[ASSentinel alloc] init];
|
||||
_preferredFrameSize = CGSizeZero;
|
||||
|
||||
_size = ASLayoutableSizeMake();
|
||||
_preferredFrameSize = CGSizeZero;
|
||||
_environmentState = ASEnvironmentStateMakeDefault();
|
||||
|
||||
_calculatedDisplayNodeLayout = std::make_shared<ASDisplayNodeLayout>();
|
||||
|
||||
_defaultLayoutTransitionDuration = 0.2;
|
||||
_defaultLayoutTransitionDelay = 0.0;
|
||||
_defaultLayoutTransitionOptions = UIViewAnimationOptionBeginFromCurrentState;
|
||||
@@ -449,11 +462,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
return !(_hierarchyState & ASHierarchyStateRasterized);
|
||||
}
|
||||
|
||||
- (BOOL)__shouldSize
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (UIView *)_viewToLoad
|
||||
{
|
||||
UIView *view;
|
||||
@@ -639,46 +647,72 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
return _flags.layerBacked;
|
||||
}
|
||||
|
||||
#pragma mark - Layout measurement calculation
|
||||
#pragma mark - Layout measurement and sizing
|
||||
|
||||
- (CGSize)measure:(CGSize)constrainedSize
|
||||
{
|
||||
return [self measureWithSizeRange:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayoutableSize)size
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (! [self shouldMeasureWithSizeRange:constrainedSize]) {
|
||||
ASDisplayNodeAssertNotNil(_calculatedLayout, @"-[ASDisplayNode measureWithSizeRange:] _layout should not be nil! %@", self);
|
||||
return _calculatedLayout ? : [ASLayout layoutWithLayoutableObject:self constrainedSizeRange:constrainedSize size:CGSizeZero];
|
||||
return _size;
|
||||
}
|
||||
|
||||
- (void)setSize:(ASLayoutableSize)size
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (ASLayoutableSizeEqualToLayoutableSize(_size, size) == NO) {
|
||||
_size = size;
|
||||
[self invalidateCalculatedLayout];
|
||||
}
|
||||
}
|
||||
|
||||
ASLayoutableSizeForwarding
|
||||
ASLayoutableSizeHelperForwarding
|
||||
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
return [self measureWithSizeRange:constrainedSize];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:parentSize] == NO) {
|
||||
ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _layout should not be nil! %@", self);
|
||||
return _calculatedDisplayNodeLayout->layout ? : [ASLayout layoutWithLayoutable:self size:{0, 0}];
|
||||
}
|
||||
|
||||
[self cancelLayoutTransition];
|
||||
|
||||
ASLayout *previousLayout = _calculatedLayout;
|
||||
ASLayout *newLayout = [self calculateLayoutThatFits:constrainedSize];
|
||||
|
||||
// Prepare for layout transition
|
||||
auto previousLayout = _calculatedDisplayNodeLayout;
|
||||
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
|
||||
[self calculateLayoutThatFits:constrainedSize restrictedToSize:_size relativeToParentSize:parentSize],
|
||||
constrainedSize,
|
||||
parentSize
|
||||
);
|
||||
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
|
||||
pendingLayout:newLayout
|
||||
pendingLayout:pendingLayout
|
||||
previousLayout:previousLayout];
|
||||
|
||||
// Only complete the pending layout transition if the node is not a subnode of a node that is currently
|
||||
// in a layout transition
|
||||
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState) == NO) {
|
||||
// Complete the pending layout transition immediately
|
||||
[self _completePendingLayoutTransition];
|
||||
}
|
||||
|
||||
ASDisplayNodeAssertNotNil(newLayout, @"-[ASDisplayNode measureWithSizeRange:] newLayout should not be nil! %@", self);
|
||||
return newLayout;
|
||||
ASDisplayNodeAssertNotNil(pendingLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newLayout should not be nil! %@", self);
|
||||
return pendingLayout->layout;
|
||||
}
|
||||
|
||||
- (BOOL)shouldMeasureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (BOOL)shouldCalculateLayoutWithConstrainedSize:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (![self __shouldSize]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
// Don't remeasure if in layout pending state and a new transition already started
|
||||
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
|
||||
ASLayoutableContext context = ASLayoutableGetCurrentContext();
|
||||
if (ASLayoutableContextIsNull(context) || _pendingTransitionID != context.transitionID) {
|
||||
@@ -686,15 +720,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
}
|
||||
}
|
||||
|
||||
// Only generate a new layout if:
|
||||
// - The current layout is dirty
|
||||
// - The passed constrained size is different than the layout's constrained size
|
||||
return ([self _hasDirtyLayout] || !ASSizeRangeEqualToSizeRange(constrainedSize, _calculatedLayout.constrainedSizeRange));
|
||||
}
|
||||
|
||||
- (BOOL)_hasDirtyLayout
|
||||
{
|
||||
return _calculatedLayout == nil || _calculatedLayout.isDirty;
|
||||
// Check if display node layout is still valid
|
||||
return _calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize) == NO;
|
||||
}
|
||||
|
||||
- (ASLayoutableType)layoutableType
|
||||
@@ -727,14 +754,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
shouldMeasureAsync:(BOOL)shouldMeasureAsync
|
||||
measurementCompletion:(void(^)())completion
|
||||
{
|
||||
if (_calculatedLayout == nil) {
|
||||
// constrainedSizeRange returns a struct and is invalid to call on nil.
|
||||
// Defaulting to CGSizeZero can cause negative values in client layout code.
|
||||
if (_calculatedDisplayNodeLayout->layout == nil) {
|
||||
// No measure pass happened before, it's not possible to reuse the constrained size for the transition
|
||||
// Using CGSizeZero for the sizeRange can cause negative values in client layout code.
|
||||
return;
|
||||
}
|
||||
|
||||
[self invalidateCalculatedLayout];
|
||||
[self transitionLayoutWithSizeRange:_calculatedLayout.constrainedSizeRange
|
||||
[self transitionLayoutWithSizeRange:_calculatedDisplayNodeLayout->constrainedSize
|
||||
animated:animated
|
||||
shouldMeasureAsync:shouldMeasureAsync
|
||||
measurementCompletion:completion];
|
||||
@@ -747,7 +774,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
measurementCompletion:(void(^)())completion
|
||||
{
|
||||
// Passed constrainedSize is the the same as the node's current constrained size it's a noop
|
||||
if ([self shouldMeasureWithSizeRange:constrainedSize] == NO) {
|
||||
ASDisplayNodeAssertMainThread();
|
||||
if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:constrainedSize.max] == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -758,6 +786,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
int32_t transitionID = [self _startNewTransition];
|
||||
|
||||
// Move all subnodes in a pending state
|
||||
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
||||
ASDisplayNodeAssert([node _isTransitionInProgress] == NO, @"Can't start a transition when one of the subnodes is performing one.");
|
||||
node.hierarchyState |= ASHierarchyStateLayoutPending;
|
||||
@@ -777,7 +806,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
BOOL automaticallyManagesSubnodesDisabled = (self.automaticallyManagesSubnodes == NO);
|
||||
self.automaticallyManagesSubnodes = YES; // Temporary flag for 1.9.x
|
||||
newLayout = [self calculateLayoutThatFits:constrainedSize];
|
||||
newLayout = [self calculateLayoutThatFits:constrainedSize
|
||||
restrictedToSize:_size
|
||||
relativeToParentSize:constrainedSize.max];
|
||||
if (automaticallyManagesSubnodesDisabled) {
|
||||
self.automaticallyManagesSubnodes = NO; // Temporary flag for 1.9.x
|
||||
}
|
||||
@@ -797,10 +828,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
if ([self _shouldAbortTransitionWithID:transitionID]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update display node layout
|
||||
auto previousLayout = _calculatedDisplayNodeLayout;
|
||||
auto pendingLayout = std::make_shared<ASDisplayNodeLayout>(
|
||||
newLayout,
|
||||
constrainedSize,
|
||||
constrainedSize.max
|
||||
);
|
||||
[self setCalculatedDisplayNodeLayout:pendingLayout];
|
||||
|
||||
ASLayout *previousLayout = _calculatedLayout;
|
||||
[self setCalculatedLayout:newLayout];
|
||||
|
||||
// Apply complete layout transitions for all subnodes
|
||||
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
|
||||
[node _completePendingLayoutTransition];
|
||||
node.hierarchyState &= (~ASHierarchyStateLayoutPending);
|
||||
@@ -808,13 +846,14 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
[self _finishOrCancelTransition];
|
||||
|
||||
// Measurement pass completion
|
||||
if (completion) {
|
||||
completion();
|
||||
}
|
||||
|
||||
// Setup pending layout transition for animation
|
||||
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
|
||||
pendingLayout:newLayout
|
||||
pendingLayout:pendingLayout
|
||||
previousLayout:previousLayout];
|
||||
// Setup context for pending layout transition. we need to hold a strong reference to the context
|
||||
_pendingLayoutTransitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated
|
||||
@@ -916,8 +955,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
}
|
||||
|
||||
/*
|
||||
* Hook for subclasse to perform an animation based on the given ASContextTransitioning. By default a fade in and out
|
||||
* animation is provided.
|
||||
* Hook for subclasses to perform an animation based on the given ASContextTransitioning. By default a fade in and out
|
||||
* animation is provided.
|
||||
*/
|
||||
- (void)animateLayoutTransition:(id<ASContextTransitioning>)context
|
||||
{
|
||||
@@ -930,7 +969,6 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
ASDisplayNode *node = self;
|
||||
|
||||
NSAssert(node.isNodeLoaded == YES, @"Invalid node state");
|
||||
NSAssert([context isAnimated] == YES, @"Can't animate a non-animatable context");
|
||||
|
||||
NSArray<ASDisplayNode *> *removedSubnodes = [context removedSubnodes];
|
||||
NSMutableArray<UIView *> *removedViews = [NSMutableArray array];
|
||||
@@ -1031,7 +1069,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (_pendingLayoutTransition) {
|
||||
[self setCalculatedLayout:_pendingLayoutTransition.pendingLayout];
|
||||
[self setCalculatedDisplayNodeLayout:_pendingLayoutTransition.pendingLayout];
|
||||
[self _completeLayoutTransition:_pendingLayoutTransition];
|
||||
}
|
||||
[self _pendingLayoutTransitionDidComplete];
|
||||
@@ -1072,7 +1110,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
if (_placeholderEnabled && [self _displaysAsynchronously] && self.contents == nil) {
|
||||
|
||||
// Zero-sized nodes do not require a placeholder.
|
||||
CGSize layoutSize = (_calculatedLayout ? _calculatedLayout.size : CGSizeZero);
|
||||
ASLayout *layout = _calculatedDisplayNodeLayout->layout;
|
||||
CGSize layoutSize = (layout ? layout.size : CGSizeZero);
|
||||
if (CGSizeEqualToSize(layoutSize, CGSizeZero)) {
|
||||
return;
|
||||
}
|
||||
@@ -1263,8 +1302,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
|
||||
__instanceLock__.lock();
|
||||
|
||||
if (_calculatedLayout == nil) {
|
||||
// Can't proceed without a layout as no constrained size would be available
|
||||
if (_calculatedDisplayNodeLayout->layout == nil) {
|
||||
// Can't proceed without a layout as no constrained size would be available. If not layout exists at this moment
|
||||
// no measurement pass did happen just bail out for now
|
||||
__instanceLock__.unlock();
|
||||
return;
|
||||
}
|
||||
@@ -1281,11 +1321,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
}
|
||||
|
||||
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
|
||||
[self measureWithSizeRange:_calculatedLayout.constrainedSizeRange];
|
||||
|
||||
[self layoutThatFits:_calculatedDisplayNodeLayout->constrainedSize];
|
||||
|
||||
CGRect oldBounds = self.bounds;
|
||||
CGSize oldSize = oldBounds.size;
|
||||
CGSize newSize = _calculatedLayout.size;
|
||||
CGSize newSize = _calculatedDisplayNodeLayout->layout.size;
|
||||
|
||||
if (! CGSizeEqualToSize(oldSize, newSize)) {
|
||||
self.bounds = (CGRect){ oldBounds.origin, newSize };
|
||||
@@ -1346,8 +1386,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
CGSize calculatedLayoutSize = CGSizeZero;
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
hasDirtyLayout = [self _hasDirtyLayout];
|
||||
calculatedLayoutSize = _calculatedLayout.size;
|
||||
hasDirtyLayout = _calculatedDisplayNodeLayout->isDirty();
|
||||
calculatedLayoutSize = _calculatedDisplayNodeLayout->layout.size;
|
||||
}
|
||||
|
||||
// If no measure pass happened or the bounds changed between layout passes we manually trigger a measurement pass
|
||||
@@ -1356,8 +1396,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
if (CGRectEqualToRect(bounds, CGRectZero)) {
|
||||
LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
|
||||
} else {
|
||||
// This is a no op if the bounds size is the same as the cosntrained size we used to create the layout previously
|
||||
[self measureWithSizeRange:ASSizeRangeMake(bounds.size, bounds.size)];
|
||||
[self layoutThatFits:ASSizeRangeMake(bounds.size)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2215,6 +2254,14 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
|
||||
#pragma mark - For Subclasses
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize
|
||||
{
|
||||
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutableSizeResolve(_size, parentSize));
|
||||
return [self calculateLayoutThatFits:resolvedRange];
|
||||
}
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
||||
@@ -2231,14 +2278,14 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
ASEnvironmentStatePropagateDown(layoutSpec, self.environmentTraitCollection);
|
||||
|
||||
layoutSpec.isMutable = NO;
|
||||
ASLayout *layout = [layoutSpec measureWithSizeRange:constrainedSize];
|
||||
ASLayout *layout = [layoutSpec layoutThatFits:constrainedSize];
|
||||
ASDisplayNodeAssertNotNil(layout, @"[ASLayoutSpec measureWithSizeRange:] should never return nil! %@, %@", self, layoutSpec);
|
||||
|
||||
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.
|
||||
BOOL isFinalLayoutable = (layout.layoutableObject != self);
|
||||
BOOL isFinalLayoutable = (layout.layoutable != self);
|
||||
if (isFinalLayoutable) {
|
||||
layout.position = CGPointZero;
|
||||
layout = [ASLayout layoutWithLayoutableObject:self constrainedSizeRange:constrainedSize size:layout.size sublayouts:@[layout]];
|
||||
layout = [ASLayout layoutWithLayoutable:self size:layout.size sublayouts:@[layout]];
|
||||
#if LAYOUT_VALIDATION
|
||||
ASLayoutableValidateLayout(layout);
|
||||
#endif
|
||||
@@ -2248,9 +2295,7 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
// If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used,
|
||||
// assume that the default implementation of -calculateSizeThatFits: returns it.
|
||||
CGSize size = [self calculateSizeThatFits:constrainedSize.max];
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:ASSizeRangeClamp(constrainedSize, size)];
|
||||
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:nil];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2259,7 +2304,13 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
__ASDisplayNodeCheckForLayoutMethodOverrides;
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _preferredFrameSize;
|
||||
|
||||
// Handle deprecated preferred frame size.
|
||||
if (CGSizeEqualToSize(_preferredFrameSize, CGSizeZero) == NO) {
|
||||
return _preferredFrameSize;
|
||||
}
|
||||
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
@@ -2276,41 +2327,48 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
return [[ASLayoutSpec alloc] init];
|
||||
}
|
||||
|
||||
- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
|
||||
{
|
||||
// For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided
|
||||
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");
|
||||
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_layoutSpecBlock = [layoutSpecBlock copy];
|
||||
}
|
||||
|
||||
- (ASLayoutSpecBlock)layoutSpecBlock
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _layoutSpecBlock;
|
||||
}
|
||||
|
||||
- (ASLayout *)calculatedLayout
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _calculatedLayout;
|
||||
return _calculatedDisplayNodeLayout->layout;
|
||||
}
|
||||
|
||||
- (void)setCalculatedLayout:(ASLayout *)calculatedLayout
|
||||
- (void)setCalculatedDisplayNodeLayout:(std::shared_ptr<ASDisplayNodeLayout>)displayNodeLayout
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
ASDisplayNodeAssertTrue(calculatedLayout.layoutableObject == self);
|
||||
ASDisplayNodeAssertTrue(calculatedLayout.size.width >= 0.0);
|
||||
ASDisplayNodeAssertTrue(calculatedLayout.size.height >= 0.0);
|
||||
ASDisplayNodeAssertTrue(displayNodeLayout->layout.layoutable == self);
|
||||
ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.width >= 0.0);
|
||||
ASDisplayNodeAssertTrue(displayNodeLayout->layout.size.height >= 0.0);
|
||||
|
||||
_calculatedLayout = calculatedLayout;
|
||||
_calculatedDisplayNodeLayout = displayNodeLayout;
|
||||
}
|
||||
|
||||
- (CGSize)calculatedSize
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _calculatedLayout.size;
|
||||
return _calculatedDisplayNodeLayout->layout.size;
|
||||
}
|
||||
|
||||
- (ASSizeRange)constrainedSizeForCalculatedLayout
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _calculatedLayout.constrainedSizeRange;
|
||||
}
|
||||
|
||||
- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
|
||||
{
|
||||
// For now there should never be a overwrite of layoutSpecThatFits: and a layoutSpecThatFitsBlock: be provided
|
||||
ASDisplayNodeAssert(!(_methodOverrides & ASDisplayNodeMethodOverrideLayoutSpecThatFits), @"Overwriting layoutSpecThatFits: and providing a layoutSpecBlock block is currently not supported");
|
||||
|
||||
_layoutSpecBlock = layoutSpecBlock;
|
||||
return _calculatedDisplayNodeLayout->constrainedSize;
|
||||
}
|
||||
|
||||
- (void)setPendingTransitionID:(int32_t)pendingTransitionID
|
||||
@@ -2331,7 +2389,10 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (! CGSizeEqualToSize(_preferredFrameSize, preferredFrameSize)) {
|
||||
_preferredFrameSize = preferredFrameSize;
|
||||
self.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(_preferredFrameSize);
|
||||
|
||||
self.width = ASDimensionMake(preferredFrameSize.width);
|
||||
self.height = ASDimensionMake(preferredFrameSize.height);
|
||||
|
||||
[self invalidateCalculatedLayout];
|
||||
}
|
||||
}
|
||||
@@ -2363,9 +2424,9 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
|
||||
// This will cause the next call to -measureWithSizeRange: to actually compute a new layout
|
||||
// instead of returning the current layout
|
||||
_calculatedLayout.dirty = YES;
|
||||
// This will cause the next call to -layoutThatFits:parentSize: to compute a new layout instead of returning
|
||||
// the cached layout in case the constrained or parent size did not change
|
||||
_calculatedDisplayNodeLayout->invalidate();
|
||||
}
|
||||
|
||||
- (void)__didLoad
|
||||
@@ -2756,47 +2817,11 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_applyPendingLayoutContext
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
if (_pendingLayoutTransition) {
|
||||
[self _applyLayout:_pendingLayoutTransition.pendingLayout layoutTransition:_pendingLayoutTransition];
|
||||
_pendingLayoutTransition = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_applyLayout:(ASLayout *)layout layoutTransition:(ASLayoutTransition *)layoutTransition
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_calculatedLayout = layout;
|
||||
|
||||
ASDisplayNodeAssertTrue(layout.layoutableObject == self);
|
||||
ASDisplayNodeAssertTrue(layout.size.width >= 0.0);
|
||||
ASDisplayNodeAssertTrue(layout.size.height >= 0.0);
|
||||
|
||||
if (layoutTransition == nil || self.automaticallyManagesSubnodes == NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trampoline to the main thread if necessary
|
||||
if (ASDisplayNodeThreadIsMain() == NO && layoutTransition.isSynchronous == NO) {
|
||||
|
||||
// Subnode insertions and removals need to happen always on the main thread if at least one subnode is already loaded
|
||||
ASPerformBlockOnMainThread(^{
|
||||
[layoutTransition commitTransition];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[layoutTransition commitTransition];
|
||||
}
|
||||
|
||||
- (void)layout
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
|
||||
if ([self _hasDirtyLayout]) {
|
||||
if (_calculatedDisplayNodeLayout->isDirty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2805,8 +2830,8 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock)
|
||||
|
||||
- (void)__layoutSublayouts
|
||||
{
|
||||
for (ASLayout *subnodeLayout in _calculatedLayout.sublayouts) {
|
||||
((ASDisplayNode *)subnodeLayout.layoutableObject).frame = [subnodeLayout frame];
|
||||
for (ASLayout *subnodeLayout in _calculatedDisplayNodeLayout->layout.sublayouts) {
|
||||
((ASDisplayNode *)subnodeLayout.layoutable).frame = subnodeLayout.frame;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3205,6 +3230,19 @@ ASEnvironmentLayoutExtensibilityForwarding
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
- (CGSize)measure:(CGSize)constrainedSize
|
||||
{
|
||||
return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASDisplayNode (Debugging)
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
|
||||
#import "_ASDisplayLayer.h"
|
||||
#import "ASAssert.h"
|
||||
#import "ASDimension.h"
|
||||
#import "ASDisplayNode+Subclasses.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASDisplayNode+Beta.h"
|
||||
#import "ASLayout.h"
|
||||
#import "ASTextNode.h"
|
||||
#import "ASImageNode+AnimatedImagePrivate.h"
|
||||
|
||||
@@ -186,13 +188,23 @@ struct ASImageNodeDrawParameters {
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
// if a preferredFrameSize is set, call the superclass to return that instead of using the image size.
|
||||
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO)
|
||||
return [super calculateSizeThatFits:constrainedSize];
|
||||
else if (_image)
|
||||
return _image.size;
|
||||
else
|
||||
return CGSizeZero;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// If a preferredFrameSize is set, call the superclass to return that instead of using the image size.
|
||||
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) {
|
||||
#if DEBUG
|
||||
NSLog(@"Using -[ASDisplayNode preferredFrameSize] is deprecated.");
|
||||
#endif
|
||||
return self.preferredFrameSize;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
if (_image == nil) {
|
||||
return constrainedSize;
|
||||
}
|
||||
|
||||
return _image.size;
|
||||
}
|
||||
|
||||
#pragma mark - Setter / Getter
|
||||
@@ -645,7 +657,7 @@ static ASDN::Mutex cacheLock;
|
||||
|
||||
if (_debugLabelNode) {
|
||||
CGSize boundsSize = self.bounds.size;
|
||||
CGSize debugLabelSize = [_debugLabelNode measure:boundsSize];
|
||||
CGSize debugLabelSize = [_debugLabelNode layoutThatFits:ASSizeRangeMake(CGSizeZero, boundsSize)].size;
|
||||
CGPoint debugLabelOrigin = CGPointMake(boundsSize.width - debugLabelSize.width,
|
||||
boundsSize.height - debugLabelSize.height);
|
||||
_debugLabelNode.frame = (CGRect) {debugLabelOrigin, debugLabelSize};
|
||||
|
||||
@@ -392,21 +392,14 @@
|
||||
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
CGSize size = self.preferredFrameSize;
|
||||
if (CGSizeEqualToSize(size, CGSizeZero)) {
|
||||
size = constrainedSize;
|
||||
|
||||
// FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc)
|
||||
// These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value.
|
||||
if (!isValidForLayout(size.width)) {
|
||||
size.width = 100.0;
|
||||
}
|
||||
if (!isValidForLayout(size.height)) {
|
||||
size.height = 100.0;
|
||||
}
|
||||
// FIXME: Need a better way to allow maps to take up the right amount of space in a layout (sizeRange, etc)
|
||||
// These fallbacks protect against inheriting a constrainedSize that contains a CGFLOAT_MAX value.
|
||||
if (!ASIsCGSizeValidForLayout(constrainedSize)) {
|
||||
//ASDisplayNodeAssert(NO, @"Invalid width or height in ASMapNode");
|
||||
constrainedSize = CGSizeZero;
|
||||
}
|
||||
[self setSnapshotSizeWithReloadIfNeeded:size];
|
||||
return size;
|
||||
[self setSnapshotSizeWithReloadIfNeeded:constrainedSize];
|
||||
return constrainedSize;
|
||||
}
|
||||
|
||||
- (void)calculatedLayoutDidChange
|
||||
|
||||
@@ -1130,7 +1130,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
CGSizeMake(_nodesConstrainedWidth, delegateConstrainedSize.max.height));
|
||||
} else {
|
||||
constrainedSize = ASSizeRangeMake(CGSizeMake(_nodesConstrainedWidth, 0),
|
||||
CGSizeMake(_nodesConstrainedWidth, FLT_MAX));
|
||||
CGSizeMake(_nodesConstrainedWidth, CGFLOAT_MAX));
|
||||
}
|
||||
return constrainedSize;
|
||||
}
|
||||
@@ -1186,7 +1186,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
|
||||
// Also, in many cases, some nodes may not need to be re-measured at all, such as when user enters and then immediately leaves editing mode.
|
||||
// To avoid premature optimization and making such assumption, as well as to keep ASTableView simple, re-measurement is strictly done on main.
|
||||
[self beginUpdates];
|
||||
CGSize calculatedSize = [[node measureWithSizeRange:constrainedSize] size];
|
||||
const CGSize calculatedSize = [node layoutThatFits:constrainedSize].size;
|
||||
node.frame = CGRectMake(0, 0, calculatedSize.width, calculatedSize.height);
|
||||
[self endUpdates];
|
||||
}
|
||||
|
||||
@@ -240,10 +240,17 @@ static NSString * const kRate = @"rate";
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
CGSize calculatedSize = constrainedSize;
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// if a preferredFrameSize is set, call the superclass to return that instead of using the image size.
|
||||
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO)
|
||||
if (CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero) == NO) {
|
||||
#if DEBUG
|
||||
NSLog(@"Using -[ASDisplayNde preferredFrameSize] is deprecated.");
|
||||
#endif
|
||||
calculatedSize = self.preferredFrameSize;
|
||||
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// Prevent crashes through if infinite width or height
|
||||
if (isinf(calculatedSize.width) || isinf(calculatedSize.height)) {
|
||||
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoNode");
|
||||
@@ -251,8 +258,9 @@ static NSString * const kRate = @"rate";
|
||||
}
|
||||
|
||||
if (_playerNode) {
|
||||
_playerNode.preferredFrameSize = calculatedSize;
|
||||
[_playerNode measure:calculatedSize];
|
||||
_playerNode.width = ASDimensionMake(calculatedSize.width);
|
||||
_playerNode.height = ASDimensionMake(calculatedSize.height);
|
||||
[_playerNode layoutThatFits:ASSizeRangeMake(CGSizeZero, calculatedSize)];
|
||||
}
|
||||
|
||||
return calculatedSize;
|
||||
|
||||
@@ -324,7 +324,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
{
|
||||
if (_playbackButtonNode == nil) {
|
||||
_playbackButtonNode = [[ASDefaultPlaybackButton alloc] init];
|
||||
_playbackButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
|
||||
_playbackButtonNode.width = ASDimensionMakeWithPoints(16.0);
|
||||
_playbackButtonNode.height = ASDimensionMakeWithPoints(22.0);
|
||||
|
||||
if (_delegateFlags.delegatePlaybackButtonTint) {
|
||||
_playbackButtonNode.tintColor = [_delegate videoPlayerNodePlaybackButtonTint:self];
|
||||
} else {
|
||||
@@ -598,7 +600,9 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
return spinnnerView;
|
||||
}];
|
||||
_spinnerNode.preferredFrameSize = CGSizeMake(44.0, 44.0);
|
||||
|
||||
_spinnerNode.width = ASDimensionMakeWithPoints(44.0);
|
||||
_spinnerNode.height = ASDimensionMakeWithPoints(44.0);
|
||||
|
||||
[self addSubnode:_spinnerNode];
|
||||
[self setNeedsLayout];
|
||||
@@ -689,23 +693,21 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
return controls;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Layout
|
||||
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGSize maxSize = constrainedSize.max;
|
||||
if (!CGSizeEqualToSize(self.preferredFrameSize, CGSizeZero)) {
|
||||
maxSize = self.preferredFrameSize;
|
||||
}
|
||||
|
||||
// Prevent crashes through if infinite width or height
|
||||
if (isinf(maxSize.width) || isinf(maxSize.height)) {
|
||||
ASDisplayNodeAssert(NO, @"Infinite width or height in ASVideoPlayerNode");
|
||||
maxSize = CGSizeZero;
|
||||
}
|
||||
_videoNode.preferredFrameSize = maxSize;
|
||||
_videoNode.size = ASLayoutableSizeMakeFromCGSize(maxSize);
|
||||
|
||||
ASLayoutSpec *layoutSpec;
|
||||
|
||||
if (_delegateFlags.delegateLayoutSpecForControls) {
|
||||
layoutSpec = [_delegate videoPlayerNodeLayoutSpec:self forControls:_cachedControls forMaximumSize:maxSize];
|
||||
} else {
|
||||
@@ -716,21 +718,21 @@ static void *ASVideoPlayerNodeContext = &ASVideoPlayerNodeContext;
|
||||
|
||||
if (_spinnerNode) {
|
||||
ASCenterLayoutSpec *centerLayoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:_spinnerNode];
|
||||
centerLayoutSpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
|
||||
centerLayoutSpec.size = ASLayoutableSizeMakeFromCGSize(maxSize);
|
||||
[children addObject:centerLayoutSpec];
|
||||
}
|
||||
|
||||
ASOverlayLayoutSpec *overlaySpec = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_videoNode overlay:layoutSpec];
|
||||
overlaySpec.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(maxSize);
|
||||
|
||||
overlaySpec.size = ASLayoutableSizeMakeFromCGSize(maxSize);
|
||||
[children addObject:overlaySpec];
|
||||
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
||||
}
|
||||
|
||||
- (ASLayoutSpec*)defaultLayoutSpecThatFits:(CGSize)maxSize
|
||||
- (ASLayoutSpec *)defaultLayoutSpecThatFits:(CGSize)maxSize
|
||||
{
|
||||
_scrubberNode.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
|
||||
_scrubberNode.width = ASDimensionMakeWithPoints(maxSize.width);
|
||||
_scrubberNode.height = ASDimensionMakeWithPoints(44.0);
|
||||
|
||||
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
|
||||
spacer.flexGrow = YES;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "ASAvailability.h"
|
||||
#import "ASDisplayNodeInternal.h"
|
||||
#import "ASDisplayNode+FrameworkPrivate.h"
|
||||
#import "ASLayout.h"
|
||||
#import "ASTraitCollection.h"
|
||||
#import "ASEnvironmentInternal.h"
|
||||
#import "ASRangeControllerUpdateRangeProtocol+Beta.h"
|
||||
@@ -105,7 +106,7 @@
|
||||
[self progagateNewEnvironmentTraitCollection:environmentTraitCollection];
|
||||
}];
|
||||
} else {
|
||||
[_node measureWithSizeRange:[self nodeConstrainedSize]];
|
||||
[_node layoutThatFits:[self nodeConstrainedSize]];
|
||||
}
|
||||
|
||||
if (!AS_AT_LEAST_IOS9) {
|
||||
@@ -132,7 +133,7 @@ ASVisibilityDidMoveToParentViewController;
|
||||
// We do this early layout because we need to get any ASCollectionNodes etc. into the
|
||||
// hierarchy before UIKit applies the scroll view inset adjustments, if you are using
|
||||
// automatic subnode management.
|
||||
[_node measureWithSizeRange:[self nodeConstrainedSize]];
|
||||
[_node layoutThatFits:[self nodeConstrainedSize]];
|
||||
|
||||
[_node recursivelyFetchData];
|
||||
|
||||
@@ -302,7 +303,7 @@ ASVisibilityDepthImplementation;
|
||||
|
||||
// once we've propagated all the traits, layout this node.
|
||||
// Remeasure the node with the latest constrained size – old constrained size may be incorrect.
|
||||
[self.node measureWithSizeRange:[self nodeConstrainedSize]];
|
||||
[self.node layoutThatFits:[self nodeConstrainedSize]];
|
||||
[self.node setNeedsLayout];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import "ASDisplayNodeExtras.h"
|
||||
#import "ASWeakSet.h"
|
||||
#import "UIImage+ASConvenience.h"
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNode+Subclasses.h>
|
||||
#import <AsyncDisplayKit/CGRect+ASConvenience.h>
|
||||
#import <AsyncDisplayKit/ASDisplayNodeExtras.h>
|
||||
@@ -612,7 +613,7 @@ static BOOL __shouldShowRangeDebugOverlay = NO;
|
||||
[self setBarSubviewOrder];
|
||||
|
||||
CGRect rect = CGRectIntegral(CGRectMake(0, 0, boundsSize.width, floorf(boundsSize.height / 2.0)));
|
||||
rect.size = [_debugText measure:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)];
|
||||
rect.size = [_debugText layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))].size;
|
||||
rect.origin.x = (boundsSize.width - rect.size.width) / 2.0;
|
||||
_debugText.frame = rect;
|
||||
rect.origin.y += rect.size.height;
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
static inline ASSizeRange NodeConstrainedSizeForScrollDirection(ASCollectionView *collectionView) {
|
||||
CGSize maxSize = collectionView.bounds.size;
|
||||
if (ASScrollDirectionContainsHorizontalDirection(collectionView.scrollableDirections)) {
|
||||
maxSize.width = FLT_MAX;
|
||||
maxSize.width = CGFLOAT_MAX;
|
||||
} else {
|
||||
maxSize.height = FLT_MAX;
|
||||
maxSize.height = CGFLOAT_MAX;
|
||||
}
|
||||
return ASSizeRangeMake(CGSizeZero, maxSize);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ NSString * const ASDataControllerRowNodeKind = @"_ASDataControllerRowNodeKind";
|
||||
- (void)_layoutNode:(ASCellNode *)node withConstrainedSize:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGRect frame = CGRectZero;
|
||||
frame.size = [node measureWithSizeRange:constrainedSize].size;
|
||||
frame.size = [node layoutThatFits:constrainedSize].size;
|
||||
node.frame = frame;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
|
||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
||||
|
||||
@protocol ASEnvironment;
|
||||
@class UITraitCollection;
|
||||
@@ -40,12 +39,11 @@ typedef struct ASEnvironmentLayoutOptionsState {
|
||||
CGFloat spacingAfter;// = 0;
|
||||
BOOL flexGrow;// = NO;
|
||||
BOOL flexShrink;// = NO;
|
||||
ASRelativeDimension flexBasis;// = ASRelativeDimensionUnconstrained;
|
||||
ASDimension flexBasis;// = ASRelativeDimensionAuto;
|
||||
ASStackLayoutAlignSelf alignSelf;// = ASStackLayoutAlignSelfAuto;
|
||||
CGFloat ascender;// = 0;
|
||||
CGFloat descender;// = 0;
|
||||
|
||||
ASRelativeSizeRange sizeRange;// = ASRelativeSizeRangeMake(ASRelativeSizeMakeWithCGSize(CGSizeZero), ASRelativeSizeMakeWithCGSize(CGSizeZero));;
|
||||
CGPoint layoutPosition;// = CGPointZero;
|
||||
|
||||
struct ASEnvironmentStateExtensions _extensions;
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <CoreGraphics/CoreGraphics.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
|
||||
|
||||
@@ -39,26 +39,26 @@ static NSUInteger const kBackgroundChildIndex = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
First layout the contents, then fit the background image.
|
||||
* First layout the contents, then fit the background image.
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize
|
||||
{
|
||||
ASLayout *contentsLayout = [[self child] measureWithSizeRange:constrainedSize];
|
||||
ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize];
|
||||
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:2];
|
||||
if (self.background) {
|
||||
// Size background to exactly the same size.
|
||||
ASLayout *backgroundLayout = [self.background measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
|
||||
ASLayout *backgroundLayout = [self.background layoutThatFits:ASSizeRangeMake(contentsLayout.size)
|
||||
parentSize:parentSize];
|
||||
backgroundLayout.position = CGPointZero;
|
||||
[sublayouts addObject:backgroundLayout];
|
||||
}
|
||||
contentsLayout.position = CGPointZero;
|
||||
[sublayouts addObject:contentsLayout];
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:contentsLayout.size
|
||||
sublayouts:sublayouts];
|
||||
return [ASLayout layoutWithLayoutable:self size:contentsLayout.size sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
- (void)setBackground:(id<ASLayoutable>)background
|
||||
|
||||
@@ -11,58 +11,203 @@
|
||||
#pragma once
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
#import <AsyncDisplayKit/ASAssert.h>
|
||||
|
||||
/** A dimension relative to constraints to be provided in the future. */
|
||||
typedef NS_ENUM(NSInteger, ASRelativeDimensionType) {
|
||||
ASDISPLAYNODE_INLINE BOOL ASPointsAreValidForLayout(CGFloat points)
|
||||
{
|
||||
return ((isnormal(points) || points == 0.0) && points >= 0.0 && points < (CGFLOAT_MAX / 2.0));
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE BOOL ASIsCGSizeValidForLayout(CGSize size)
|
||||
{
|
||||
return (ASPointsAreValidForLayout(size.width) && ASPointsAreValidForLayout(size.height));
|
||||
}
|
||||
|
||||
/**
|
||||
* A dimension relative to constraints to be provided in the future.
|
||||
* A ASDimension can be one of three types:
|
||||
*
|
||||
* "Auto" - This indicated "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances.
|
||||
*
|
||||
* "Points" - Just a number. It will always resolve to exactly this amount.
|
||||
*
|
||||
* "Percent" - Multiplied to a provided parent amount to resolve a final amount.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, ASDimensionUnit) {
|
||||
/** This indicates "I have no opinion" and may be resolved in whatever way makes most sense given the circumstances. */
|
||||
ASDimensionUnitAuto,
|
||||
/** Just a number. It will always resolve to exactly this amount. This is the default type. */
|
||||
ASRelativeDimensionTypePoints,
|
||||
ASDimensionUnitPoints,
|
||||
/** Multiplied to a provided parent amount to resolve a final amount. */
|
||||
ASRelativeDimensionTypeFraction,
|
||||
ASDimensionUnitFraction,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
ASRelativeDimensionType type;
|
||||
ASDimensionUnit unit;
|
||||
CGFloat value;
|
||||
} ASRelativeDimension;
|
||||
} ASDimension;
|
||||
|
||||
/** Expresses an inclusive range of sizes. Used to provide a simple constraint to layout. */
|
||||
/**
|
||||
* Expresses an inclusive range of sizes. Used to provide a simple constraint to layout.
|
||||
*/
|
||||
typedef struct {
|
||||
CGSize min;
|
||||
CGSize max;
|
||||
} ASSizeRange;
|
||||
|
||||
extern ASRelativeDimension const ASRelativeDimensionUnconstrained;
|
||||
/**
|
||||
* A struct specifying a ASLayoutable's size. Example:
|
||||
*
|
||||
* ASLayoutableSize size = (ASLayoutableSize){
|
||||
* .width = ASDimensionMakeWithFraction(0.25),
|
||||
* .maxWidth = ASDimensionMakeWithPoints(200),
|
||||
* .minHeight = ASDimensionMakeWithFraction(0.50)
|
||||
* };
|
||||
*
|
||||
* Description: <ASLayoutableSize: exact={25%, Auto}, min={Auto, 50%}, max={200pt, Auto}>
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
ASDimension width;
|
||||
ASDimension height;
|
||||
ASDimension minWidth;
|
||||
ASDimension maxWidth;
|
||||
ASDimension minHeight;
|
||||
ASDimension maxHeight;
|
||||
} ASLayoutableSize;
|
||||
|
||||
#define isValidForLayout(x) ((isnormal(x) || x == 0.0) && x >= 0.0 && x < (CGFLOAT_MAX / 2.0))
|
||||
extern ASDimension const ASDimensionAuto;
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - ASRelativeDimension
|
||||
|
||||
extern ASRelativeDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value);
|
||||
#pragma mark - ASDimension
|
||||
|
||||
extern ASRelativeDimension ASRelativeDimensionMakeWithPoints(CGFloat points);
|
||||
/**
|
||||
* Returns a dimension with the specified type and value.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(ASDimensionUnit unit, CGFloat value)
|
||||
{
|
||||
if (unit == ASDimensionUnitPoints) {
|
||||
ASDisplayNodeCAssertPositiveReal(@"Points", value);
|
||||
} else if (unit == ASDimensionUnitFraction) {
|
||||
// TODO: Enable this assertion for 2.0. Check that there is no use case for using a larger value, e.g. to layout for a clipsToBounds = NO element.
|
||||
// ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", value);
|
||||
}
|
||||
ASDimension dimension;
|
||||
dimension.unit = unit;
|
||||
dimension.value = value;
|
||||
return dimension;
|
||||
}
|
||||
|
||||
extern ASRelativeDimension ASRelativeDimensionMakeWithFraction(CGFloat fraction);
|
||||
/**
|
||||
* Returns a dimension with the specified points value.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASDimension ASDimensionMake(CGFloat points)
|
||||
{
|
||||
return ASDimensionMake(ASDimensionUnitPoints, points);
|
||||
}
|
||||
|
||||
extern ASRelativeDimension ASRelativeDimensionCopy(ASRelativeDimension aDimension);
|
||||
/**
|
||||
* Returns a dimension by parsing the specified dimension string.
|
||||
* Examples: ASDimensionMake(@"0.5%") = ASDimensionMake(ASDimensionUnitFraction, 0.5)
|
||||
* ASDimensionMake(@"0.5pt") = ASDimensionMake(ASDimensionUnitPoints, 0.5)
|
||||
*/
|
||||
ASOVERLOADABLE extern ASDimension ASDimensionMake(NSString *dimension);
|
||||
|
||||
extern BOOL ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimension lhs, ASRelativeDimension rhs);
|
||||
/**
|
||||
* Returns a dimension with the specified points value.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE ASDimension ASDimensionMakeWithPoints(CGFloat points)
|
||||
{
|
||||
ASDisplayNodeCAssertPositiveReal(@"Points", points);
|
||||
return ASDimensionMake(ASDimensionUnitPoints, points);
|
||||
}
|
||||
|
||||
extern NSString *NSStringFromASRelativeDimension(ASRelativeDimension dimension);
|
||||
/**
|
||||
* Returns a dimension with the specified fraction value.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE ASDimension ASDimensionMakeWithFraction(CGFloat fraction)
|
||||
{
|
||||
ASDisplayNodeCAssert( 0 <= fraction && fraction <= 1.0, @"ASDimension fraction value (%f) must be between 0 and 1.", fraction);
|
||||
return ASDimensionMake(ASDimensionUnitFraction, fraction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two dimensions are equal.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE BOOL ASDimensionEqualToDimension(ASDimension lhs, ASDimension rhs)
|
||||
{
|
||||
return (lhs.unit == rhs.unit && lhs.value == rhs.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a NSString representation of a dimension.
|
||||
*/
|
||||
extern NSString *NSStringFromASDimension(ASDimension dimension);
|
||||
|
||||
/**
|
||||
* Resolve this dimension to a parent size.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE CGFloat ASDimensionResolve(ASDimension dimension, CGFloat parentSize, CGFloat autoSize)
|
||||
{
|
||||
switch (dimension.unit) {
|
||||
case ASDimensionUnitAuto:
|
||||
return autoSize;
|
||||
case ASDimensionUnitPoints:
|
||||
return dimension.value;
|
||||
case ASDimensionUnitFraction:
|
||||
return dimension.value * parentSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - NSNumber+ASDimension
|
||||
|
||||
@interface NSNumber (ASDimension)
|
||||
@property (nonatomic, readonly) ASDimension as_pointDimension;
|
||||
@property (nonatomic, readonly) ASDimension as_fractionDimension;
|
||||
@end
|
||||
|
||||
extern CGFloat ASRelativeDimensionResolve(ASRelativeDimension dimension, CGFloat parent);
|
||||
|
||||
#pragma mark - ASSizeRange
|
||||
|
||||
extern ASSizeRange ASSizeRangeMake(CGSize min, CGSize max);
|
||||
/**
|
||||
* Creates an ASSizeRange with provided min and max size.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
|
||||
{
|
||||
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
|
||||
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
|
||||
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width);
|
||||
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height);
|
||||
ASDisplayNodeCAssert(min.width <= max.width,
|
||||
@"Range min width (%f) must not be larger than max width (%f).", min.width, max.width);
|
||||
ASDisplayNodeCAssert(min.height <= max.height,
|
||||
@"Range min height (%f) must not be larger than max height (%f).", min.height, max.height);
|
||||
ASSizeRange sizeRange;
|
||||
sizeRange.min = min;
|
||||
sizeRange.max = max;
|
||||
return sizeRange;
|
||||
}
|
||||
|
||||
/** Creates an ASSizeRange with the provided size as both min and max */
|
||||
extern ASSizeRange ASSizeRangeMakeExactSize(CGSize size);
|
||||
/**
|
||||
* Creates an ASSizeRange with provided size as both min and max.
|
||||
*/
|
||||
ASOVERLOADABLE ASDISPLAYNODE_INLINE ASSizeRange ASSizeRangeMake(CGSize exactSize)
|
||||
{
|
||||
return ASSizeRangeMake(exactSize, exactSize);
|
||||
}
|
||||
|
||||
/** Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange. */
|
||||
extern CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size);
|
||||
/**
|
||||
* Clamps the provided CGSize between the [min, max] bounds of this ASSizeRange.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size)
|
||||
{
|
||||
return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)),
|
||||
MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects another size range. If the other size range does not overlap in either dimension, this size range
|
||||
@@ -70,9 +215,91 @@ extern CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size);
|
||||
*/
|
||||
extern ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRange);
|
||||
|
||||
extern BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs);
|
||||
/**
|
||||
* Returns whether two size ranges are equal in min and max size
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs)
|
||||
{
|
||||
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of a size range
|
||||
*/
|
||||
extern NSString *NSStringFromASSizeRange(ASSizeRange sizeRange);
|
||||
|
||||
|
||||
#pragma mark - ASLayoutableSize
|
||||
|
||||
/**
|
||||
* Returns an ASLayoutableSize with default values.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE ASLayoutableSize ASLayoutableSizeMake()
|
||||
{
|
||||
return (ASLayoutableSize){
|
||||
.width = ASDimensionAuto,
|
||||
.height = ASDimensionAuto,
|
||||
.minWidth = ASDimensionAuto,
|
||||
.maxWidth = ASDimensionAuto,
|
||||
.minHeight = ASDimensionAuto,
|
||||
.maxHeight = ASDimensionAuto
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an ASLayoutableSize with the specified CGSize values as width and height.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE ASLayoutableSize ASLayoutableSizeMakeFromCGSize(CGSize size)
|
||||
{
|
||||
ASLayoutableSize s = ASLayoutableSizeMake();
|
||||
s.width = ASDimensionMakeWithPoints(size.width);
|
||||
s.height = ASDimensionMakeWithPoints(size.height);
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two sizes are equal.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE BOOL ASLayoutableSizeEqualToLayoutableSize(ASLayoutableSize lhs, ASLayoutableSize rhs)
|
||||
{
|
||||
return (ASDimensionEqualToDimension(lhs.width, rhs.width)
|
||||
&& ASDimensionEqualToDimension(lhs.height, rhs.height)
|
||||
&& ASDimensionEqualToDimension(lhs.minWidth, rhs.minWidth)
|
||||
&& ASDimensionEqualToDimension(lhs.maxWidth, rhs.maxWidth)
|
||||
&& ASDimensionEqualToDimension(lhs.minHeight, rhs.minHeight)
|
||||
&& ASDimensionEqualToDimension(lhs.maxHeight, rhs.maxHeight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string formatted to contain the data from an ASLayoutableSize.
|
||||
*/
|
||||
extern NSString *NSStringFromASLayoutableSize(ASLayoutableSize size);
|
||||
|
||||
/**
|
||||
* Resolve the given size relative to a parent size and an auto size.
|
||||
* From the given size uses width, height to resolve the exact size constraint, uses the minHeight and minWidth to
|
||||
* resolve the min size constraint and the maxHeight and maxWidth to resolve the max size constraint. For every
|
||||
* dimension with unit ASDimensionUnitAuto the given autoASSizeRange value will be used.
|
||||
* Based on the calculated exact, min and max size constraints the final size range will be calculated.
|
||||
*/
|
||||
extern ASSizeRange ASLayoutableSizeResolveAutoSize(ASLayoutableSize size, const CGSize parentSize, ASSizeRange autoASSizeRange);
|
||||
|
||||
/**
|
||||
* Resolve the given size to a parent size. Uses internally ASLayoutableSizeResolveAutoSize with {INFINITY, INFINITY} as
|
||||
* as autoASSizeRange. For more information look at ASLayoutableSizeResolveAutoSize.
|
||||
*/
|
||||
ASDISPLAYNODE_INLINE ASSizeRange ASLayoutableSizeResolve(ASLayoutableSize size, const CGSize parentSize)
|
||||
{
|
||||
return ASLayoutableSizeResolveAutoSize(size, parentSize, ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY, INFINITY)));
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
/**
|
||||
* Function is deprecated. Use ASSizeRangeMakeWithExactCGSize instead.
|
||||
*/
|
||||
extern ASSizeRange ASSizeRangeMakeExactSize(CGSize size) ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
@@ -11,89 +11,164 @@
|
||||
#import "ASDimension.h"
|
||||
#import "ASAssert.h"
|
||||
|
||||
ASRelativeDimension const ASRelativeDimensionUnconstrained = {};
|
||||
#pragma mark - ASDimension
|
||||
|
||||
#pragma mark - ASRelativeDimension
|
||||
ASDimension const ASDimensionAuto = {ASDimensionUnitAuto, 0};
|
||||
|
||||
ASRelativeDimension ASRelativeDimensionMake(ASRelativeDimensionType type, CGFloat value)
|
||||
ASOVERLOADABLE ASDimension ASDimensionMake(NSString *dimension)
|
||||
{
|
||||
if (type == ASRelativeDimensionTypePoints) {
|
||||
ASDisplayNodeCAssertPositiveReal(@"Points", value);
|
||||
} else if (type == ASRelativeDimensionTypeFraction) {
|
||||
// TODO: Enable this assertion for 2.0. Check that there is no use case for using a larger value, e.g. to layout for a clipsToBounds = NO element.
|
||||
// ASDisplayNodeCAssert( 0 <= value && value <= 1.0, @"ASRelativeDimension fraction value (%f) must be between 0 and 1.", value);
|
||||
// Handle empty string
|
||||
if (dimension.length == 0) {
|
||||
return ASDimensionMake(ASDimensionUnitPoints, 0.0);
|
||||
}
|
||||
ASRelativeDimension dimension; dimension.type = type; dimension.value = value; return dimension;
|
||||
|
||||
// Handle points
|
||||
NSUInteger pointsStringLocation = [dimension rangeOfString:@"pt"].location;
|
||||
if (pointsStringLocation != NSNotFound) {
|
||||
// Check if points is at the end and remove it
|
||||
if (pointsStringLocation == (dimension.length-2)) {
|
||||
dimension = [dimension substringToIndex:(dimension.length-2)];
|
||||
return ASDimensionMake(ASDimensionUnitPoints, dimension.floatValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle fraction
|
||||
NSUInteger percentStringLocation = [dimension rangeOfString:@"%"].location;
|
||||
if (percentStringLocation != NSNotFound) {
|
||||
// Check if percent is at the end and remove it
|
||||
if (percentStringLocation == (dimension.length-1)) {
|
||||
dimension = [dimension substringToIndex:(dimension.length-1)];
|
||||
return ASDimensionMake(ASDimensionUnitFraction, dimension.floatValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Assert as parsing went wrong
|
||||
ASDisplayNodeCAssert(NO, @"Parsing dimension failed for: %@", dimension);
|
||||
return ASDimensionAuto;
|
||||
}
|
||||
|
||||
ASRelativeDimension ASRelativeDimensionMakeWithPoints(CGFloat points)
|
||||
NSString *NSStringFromASDimension(ASDimension dimension)
|
||||
{
|
||||
ASDisplayNodeCAssertPositiveReal(@"Points", points);
|
||||
return ASRelativeDimensionMake(ASRelativeDimensionTypePoints, points);
|
||||
}
|
||||
|
||||
ASRelativeDimension ASRelativeDimensionMakeWithFraction(CGFloat fraction)
|
||||
{
|
||||
// ASDisplayNodeCAssert( 0 <= fraction && fraction <= 1.0, @"ASRelativeDimension fraction value (%f) must be between 0 and 1.", fraction);
|
||||
return ASRelativeDimensionMake(ASRelativeDimensionTypeFraction, fraction);
|
||||
}
|
||||
|
||||
ASRelativeDimension ASRelativeDimensionCopy(ASRelativeDimension aDimension)
|
||||
{
|
||||
return ASRelativeDimensionMake(aDimension.type, aDimension.value);
|
||||
}
|
||||
|
||||
BOOL ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimension lhs, ASRelativeDimension rhs)
|
||||
{
|
||||
return lhs.type == rhs.type && lhs.value == rhs.value;
|
||||
}
|
||||
|
||||
NSString *NSStringFromASRelativeDimension(ASRelativeDimension dimension)
|
||||
{
|
||||
switch (dimension.type) {
|
||||
case ASRelativeDimensionTypePoints:
|
||||
switch (dimension.unit) {
|
||||
case ASDimensionUnitPoints:
|
||||
return [NSString stringWithFormat:@"%.0fpt", dimension.value];
|
||||
case ASRelativeDimensionTypeFraction:
|
||||
case ASDimensionUnitFraction:
|
||||
return [NSString stringWithFormat:@"%.0f%%", dimension.value * 100.0];
|
||||
case ASDimensionUnitAuto:
|
||||
return @"Auto";
|
||||
}
|
||||
}
|
||||
|
||||
CGFloat ASRelativeDimensionResolve(ASRelativeDimension dimension, CGFloat parent)
|
||||
|
||||
#pragma mark - NSNumber+ASDimension
|
||||
|
||||
@implementation NSNumber (ASDimension)
|
||||
|
||||
- (ASDimension)as_pointDimension
|
||||
{
|
||||
switch (dimension.type) {
|
||||
case ASRelativeDimensionTypePoints:
|
||||
return dimension.value;
|
||||
case ASRelativeDimensionTypeFraction:
|
||||
return dimension.value * parent;
|
||||
}
|
||||
return ASDimensionMake(ASDimensionUnitPoints, self.floatValue);
|
||||
}
|
||||
|
||||
- (ASDimension)as_fractionDimension
|
||||
{
|
||||
return ASDimensionMake(ASDimensionUnitFraction, self.floatValue);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - ASRelativeSize
|
||||
|
||||
/**
|
||||
* Expresses a size with relative dimensions. Only used for calculations internally in ASDimension.h
|
||||
*/
|
||||
typedef struct {
|
||||
ASDimension width;
|
||||
ASDimension height;
|
||||
} ASRelativeSize;
|
||||
|
||||
ASDISPLAYNODE_INLINE ASRelativeSize ASRelativeSizeMake(ASDimension width, ASDimension height)
|
||||
{
|
||||
ASRelativeSize size;
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
return size;
|
||||
}
|
||||
|
||||
// ** Resolve this relative size relative to a parent size. */
|
||||
ASDISPLAYNODE_INLINE CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize, CGSize autoSize)
|
||||
{
|
||||
return CGSizeMake(ASDimensionResolve(relativeSize.width, parentSize.width, autoSize.width),
|
||||
ASDimensionResolve(relativeSize.height, parentSize.height, autoSize.height));
|
||||
}
|
||||
|
||||
// ** Returns a string formatted to contain the data from an ASRelativeSize. */
|
||||
ASDISPLAYNODE_INLINE NSString *NSStringFromASRelativeSize(ASRelativeSize size)
|
||||
{
|
||||
return [NSString stringWithFormat:@"{%@, %@}",
|
||||
NSStringFromASDimension(size.width),
|
||||
NSStringFromASDimension(size.height)];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - ASLayoutableSize
|
||||
|
||||
NSString *NSStringFromASLayoutableSize(ASLayoutableSize size)
|
||||
{
|
||||
return [NSString stringWithFormat:
|
||||
@"<ASLayoutableSize: exact=%@, min=%@, max=%@>",
|
||||
NSStringFromASRelativeSize(ASRelativeSizeMake(size.width, size.height)),
|
||||
NSStringFromASRelativeSize(ASRelativeSizeMake(size.minWidth, size.minHeight)),
|
||||
NSStringFromASRelativeSize(ASRelativeSizeMake(size.maxWidth, size.maxHeight))];
|
||||
}
|
||||
|
||||
ASDISPLAYNODE_INLINE void ASLayoutableSizeConstrain(CGFloat minVal, CGFloat exactVal, CGFloat maxVal, CGFloat *outMin, CGFloat *outMax)
|
||||
{
|
||||
NSCAssert(!isnan(minVal), @"minVal must not be NaN");
|
||||
NSCAssert(!isnan(maxVal), @"maxVal must not be NaN");
|
||||
// Avoid use of min/max primitives since they're harder to reason
|
||||
// about in the presence of NaN (in exactVal)
|
||||
// Follow CSS: min overrides max overrides exact.
|
||||
|
||||
// Begin with the min/max range
|
||||
*outMin = minVal;
|
||||
*outMax = maxVal;
|
||||
if (maxVal <= minVal) {
|
||||
// min overrides max and exactVal is irrelevant
|
||||
*outMax = minVal;
|
||||
return;
|
||||
}
|
||||
if (isnan(exactVal)) {
|
||||
// no exact value, so leave as a min/max range
|
||||
return;
|
||||
}
|
||||
if (exactVal > maxVal) {
|
||||
// clip to max value
|
||||
*outMin = maxVal;
|
||||
} else if (exactVal < minVal) {
|
||||
// clip to min value
|
||||
*outMax = minVal;
|
||||
} else {
|
||||
// use exact value
|
||||
*outMin = *outMax = exactVal;
|
||||
}
|
||||
}
|
||||
|
||||
ASSizeRange ASLayoutableSizeResolveAutoSize(ASLayoutableSize size, const CGSize parentSize, ASSizeRange autoASSizeRange)
|
||||
{
|
||||
CGSize resolvedExact = ASRelativeSizeResolveSize(ASRelativeSizeMake(size.width, size.height), parentSize, {NAN, NAN});
|
||||
CGSize resolvedMin = ASRelativeSizeResolveSize(ASRelativeSizeMake(size.minWidth, size.minHeight), parentSize, autoASSizeRange.min);
|
||||
CGSize resolvedMax = ASRelativeSizeResolveSize(ASRelativeSizeMake(size.maxWidth, size.maxHeight), parentSize, autoASSizeRange.max);
|
||||
|
||||
CGSize rangeMin, rangeMax;
|
||||
ASLayoutableSizeConstrain(resolvedMin.width, resolvedExact.width, resolvedMax.width, &rangeMin.width, &rangeMax.width);
|
||||
ASLayoutableSizeConstrain(resolvedMin.height, resolvedExact.height, resolvedMax.height, &rangeMin.height, &rangeMax.height);
|
||||
return {rangeMin, rangeMax};
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - ASSizeRange
|
||||
|
||||
ASSizeRange ASSizeRangeMake(CGSize min, CGSize max)
|
||||
{
|
||||
ASDisplayNodeCAssertPositiveReal(@"Range min width", min.width);
|
||||
ASDisplayNodeCAssertPositiveReal(@"Range min height", min.height);
|
||||
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max width", max.width);
|
||||
ASDisplayNodeCAssertInfOrPositiveReal(@"Range max height", max.height);
|
||||
ASDisplayNodeCAssert(min.width <= max.width,
|
||||
@"Range min width (%f) must not be larger than max width (%f).", min.width, max.width);
|
||||
ASDisplayNodeCAssert(min.height <= max.height,
|
||||
@"Range min height (%f) must not be larger than max height (%f).", min.height, max.height);
|
||||
ASSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
|
||||
}
|
||||
|
||||
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
|
||||
{
|
||||
return ASSizeRangeMake(size, size);
|
||||
}
|
||||
|
||||
CGSize ASSizeRangeClamp(ASSizeRange sizeRange, CGSize size)
|
||||
{
|
||||
return CGSizeMake(MAX(sizeRange.min.width, MIN(sizeRange.max.width, size.width)),
|
||||
MAX(sizeRange.min.height, MIN(sizeRange.max.height, size.height)));
|
||||
}
|
||||
|
||||
struct _Range {
|
||||
CGFloat min;
|
||||
CGFloat max;
|
||||
@@ -126,14 +201,17 @@ ASSizeRange ASSizeRangeIntersect(ASSizeRange sizeRange, ASSizeRange otherSizeRan
|
||||
return {{w.min, h.min}, {w.max, h.max}};
|
||||
}
|
||||
|
||||
BOOL ASSizeRangeEqualToSizeRange(ASSizeRange lhs, ASSizeRange rhs)
|
||||
{
|
||||
return CGSizeEqualToSize(lhs.min, rhs.min) && CGSizeEqualToSize(lhs.max, rhs.max);
|
||||
}
|
||||
|
||||
NSString * NSStringFromASSizeRange(ASSizeRange sizeRange)
|
||||
NSString *NSStringFromASSizeRange(ASSizeRange sizeRange)
|
||||
{
|
||||
return [NSString stringWithFormat:@"<ASSizeRange: min=%@, max=%@>",
|
||||
NSStringFromCGSize(sizeRange.min),
|
||||
NSStringFromCGSize(sizeRange.max)];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
ASSizeRange ASSizeRangeMakeExactSize(CGSize size)
|
||||
{
|
||||
return ASSizeRangeMake(size);
|
||||
}
|
||||
|
||||
@@ -67,8 +67,15 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
Inset will compute a new constrained size for it's child after applying insets and re-positioning
|
||||
the child to respect the inset.
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize
|
||||
{
|
||||
if (self.child == nil) {
|
||||
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing.");
|
||||
return [ASLayout layoutWithLayoutable:self size:CGSizeZero];
|
||||
}
|
||||
|
||||
const CGFloat insetsX = (finiteOrZero(_insets.left) + finiteOrZero(_insets.right));
|
||||
const CGFloat insetsY = (finiteOrZero(_insets.top) + finiteOrZero(_insets.bottom));
|
||||
|
||||
@@ -88,14 +95,12 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
}
|
||||
};
|
||||
|
||||
if (self.child == nil) {
|
||||
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing.");
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:CGSizeZero];
|
||||
}
|
||||
const CGSize insetParentSize = {
|
||||
MAX(0, parentSize.width - insetsX),
|
||||
MAX(0, parentSize.height - insetsY)
|
||||
};
|
||||
|
||||
ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize];
|
||||
ASLayout *sublayout = [self.child layoutThatFits:insetConstrainedSize parentSize:insetParentSize];
|
||||
|
||||
const CGSize computedSize = ASSizeRangeClamp(constrainedSize, {
|
||||
finite(sublayout.size.width + _insets.left + _insets.right, constrainedSize.max.width),
|
||||
@@ -113,10 +118,7 @@ static CGFloat centerInset(CGFloat outer, CGFloat inner)
|
||||
|
||||
sublayout.position = CGPointMake(x, y);
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:computedSize
|
||||
sublayouts:@[sublayout]];
|
||||
return [ASLayout layoutWithLayoutable:self size:computedSize sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,10 +17,29 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
|
||||
extern CGPoint const CGPointNull;
|
||||
|
||||
extern BOOL CGPointIsNull(CGPoint point);
|
||||
|
||||
/**
|
||||
* Safely calculates the layout of the given root layoutable by guarding against nil nodes.
|
||||
* @param rootLayoutable The root node to calculate the layout for.
|
||||
* @param sizeRange The size range to calculate the root layout within.
|
||||
*/
|
||||
extern ASLayout *ASCalculateRootLayout(id<ASLayoutable> rootLayoutable, const ASSizeRange sizeRange);
|
||||
|
||||
/**
|
||||
* Safely computes the layout of the given node by guarding against nil nodes.
|
||||
* @param component The component to calculate the layout for.
|
||||
* @param sizeRange The size range to calculate the node layout within.
|
||||
* @param parentSize The parent size of the node to calculate the layout for.
|
||||
*/
|
||||
extern ASLayout *ASCalculateLayout(id<ASLayoutable> layoutable, const ASSizeRange sizeRange, const CGSize parentSize);
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
|
||||
/**
|
||||
* A node in the layout tree that represents the size and position of the object that created it (ASLayoutable).
|
||||
*/
|
||||
@@ -29,68 +48,56 @@ extern BOOL CGPointIsNull(CGPoint point);
|
||||
/**
|
||||
* The underlying object described by this layout
|
||||
*/
|
||||
@property (nonatomic, weak, readonly) id<ASLayoutable> layoutableObject;
|
||||
@property (nonatomic, weak, readonly) id<ASLayoutable> layoutable;
|
||||
|
||||
/**
|
||||
* The type of ASLayoutable that created this layout
|
||||
*/
|
||||
@property (nonatomic, readonly) ASLayoutableType type;
|
||||
@property (nonatomic, assign, readonly) ASLayoutableType type;
|
||||
|
||||
/**
|
||||
* Size of the current layout
|
||||
*/
|
||||
@property (nonatomic, readonly) CGSize size;
|
||||
@property (nonatomic, assign, readonly) CGSize size;
|
||||
|
||||
/**
|
||||
* Position in parent. Default to CGPointNull.
|
||||
*
|
||||
* @discussion When being used as a sublayout, this property must not equal CGPointNull.
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGPoint position;
|
||||
|
||||
/**
|
||||
* The size range that was use to determine the size of the layout.
|
||||
*/
|
||||
@property (nonatomic, readonly) ASSizeRange constrainedSizeRange;
|
||||
@property (nonatomic, assign, readwrite) CGPoint position;
|
||||
|
||||
/**
|
||||
* Array of ASLayouts. Each must have a valid non-null position.
|
||||
*/
|
||||
@property (nonatomic, readonly) NSArray<ASLayout *> *sublayouts;
|
||||
|
||||
/**
|
||||
* Mark the layout dirty for future regeneration.
|
||||
*/
|
||||
@property (nonatomic, getter=isDirty) BOOL dirty;
|
||||
@property (nonatomic, copy, readonly) NSArray<ASLayout *> *sublayouts;
|
||||
|
||||
/**
|
||||
* @abstract Returns a valid frame for the current layout computed with the size and position.
|
||||
* @discussion Clamps the layout's origin or position to 0 if any of the calculated values are infinite.
|
||||
*/
|
||||
@property (nonatomic, readonly) CGRect frame;
|
||||
@property (nonatomic, assign, readonly) CGRect frame;
|
||||
|
||||
/**
|
||||
* Designated initializer
|
||||
*/
|
||||
- (instancetype)initWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(NSArray *)sublayouts NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Convenience class initializer for layout construction.
|
||||
*
|
||||
* @param layoutableObject The backing ASLayoutable object.
|
||||
* @param layoutable The backing ASLayoutable object.
|
||||
* @param size The size of this layout.
|
||||
* @param position The position of this layout within its parent (if available).
|
||||
* @param sublayouts Sublayouts belong to the new layout.
|
||||
*/
|
||||
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
|
||||
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
|
||||
|
||||
/**
|
||||
* Convenience initializer that has CGPointNull position.
|
||||
@@ -98,43 +105,29 @@ extern BOOL CGPointIsNull(CGPoint point);
|
||||
* or for ASLayoutSpec subclasses that are referencing the "self" level in the layout tree,
|
||||
* or for creating a sublayout of which the position is yet to be determined.
|
||||
*
|
||||
* @param layoutableObject The backing ASLayoutable object.
|
||||
* @param size The size of this layout.
|
||||
* @param sublayouts Sublayouts belong to the new layout.
|
||||
* @param layoutable The backing ASLayoutable object.
|
||||
* @param size The size of this layout.
|
||||
* @param sublayouts Sublayouts belong to the new layout.
|
||||
*/
|
||||
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
|
||||
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
|
||||
|
||||
/**
|
||||
* Convenience that has CGPointNull position and no sublayouts.
|
||||
* Convenience that has CGPointNull position and no sublayouts.
|
||||
* Best used for creating a layout that has no sublayouts, and is either a root one
|
||||
* or a sublayout of which the position is yet to be determined.
|
||||
*
|
||||
* @param layoutableObject The backing ASLayoutable object.
|
||||
* @param size The size of this layout.
|
||||
* @param layoutable The backing ASLayoutable object.
|
||||
* @param size The size of this layout.
|
||||
*/
|
||||
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size;
|
||||
|
||||
/**
|
||||
* Convenience initializer that is flattened and has CGPointNull position.
|
||||
*
|
||||
* @param layoutableObject The backing ASLayoutable object.
|
||||
* @param size The size of this layout.
|
||||
* @param sublayouts Sublayouts belong to the new layout.
|
||||
*/
|
||||
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
|
||||
|
||||
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size;
|
||||
/**
|
||||
* Convenience initializer that creates a layout based on the values of the given layout, with a new position
|
||||
* @param layout The layout to use to create the new layout
|
||||
* @param position The position of the new layout
|
||||
*
|
||||
* @param layout The layout to use to create the new layout
|
||||
* @param position The position of the new layout
|
||||
*/
|
||||
+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position;
|
||||
|
||||
@@ -145,6 +138,14 @@ extern BOOL CGPointIsNull(CGPoint point);
|
||||
|
||||
@end
|
||||
|
||||
@interface ASLayout (Unavailable)
|
||||
|
||||
- (instancetype)init __unavailable;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Debugging
|
||||
|
||||
@interface ASLayout (Debugging)
|
||||
|
||||
/**
|
||||
@@ -154,10 +155,4 @@ extern BOOL CGPointIsNull(CGPoint point);
|
||||
|
||||
@end
|
||||
|
||||
@interface ASLayout (Unavailable)
|
||||
|
||||
- (instancetype)init __unavailable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -51,38 +51,37 @@ static inline NSString * descriptionIndents(NSUInteger indents)
|
||||
|
||||
@dynamic frame, type;
|
||||
|
||||
- (instancetype)initWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(NSArray *)sublayouts
|
||||
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
|
||||
{
|
||||
NSParameterAssert(layoutable);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
NSParameterAssert(layoutableObject);
|
||||
#if DEBUG
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
ASDisplayNodeAssert(CGPointIsNull(sublayout.position) == NO, @"Invalid position is not allowed in sublayout.");
|
||||
}
|
||||
#endif
|
||||
|
||||
_layoutableObject = layoutableObject;
|
||||
_layoutable = layoutable;
|
||||
|
||||
if (!isValidForLayout(size.width) || !isValidForLayout(size.height)) {
|
||||
ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Production will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutableObject);
|
||||
if (!ASIsCGSizeValidForLayout(size)) {
|
||||
ASDisplayNodeAssert(NO, @"layoutSize is invalid and unsafe to provide to Core Animation! Release configurations will force to 0, 0. Size = %@, node = %@", NSStringFromCGSize(size), layoutable);
|
||||
size = CGSizeZero;
|
||||
} else {
|
||||
size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
|
||||
}
|
||||
_constrainedSizeRange = sizeRange;
|
||||
_size = size;
|
||||
_dirty = NO;
|
||||
|
||||
if (CGPointIsNull(position) == NO) {
|
||||
_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y));
|
||||
} else {
|
||||
_position = position;
|
||||
}
|
||||
|
||||
_sublayouts = sublayouts != nil ? [sublayouts copy] : @[];
|
||||
_flattened = NO;
|
||||
}
|
||||
@@ -97,61 +96,41 @@ static inline NSString * descriptionIndents(NSUInteger indents)
|
||||
|
||||
#pragma mark - Class Constructors
|
||||
|
||||
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(NSArray *)sublayouts
|
||||
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size
|
||||
position:(CGPoint)position
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
|
||||
{
|
||||
return [[self alloc] initWithLayoutableObject:layoutableObject
|
||||
constrainedSizeRange:sizeRange
|
||||
size:size
|
||||
position:position
|
||||
sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
sublayouts:(NSArray *)sublayouts
|
||||
{
|
||||
return [self layoutWithLayoutableObject:layoutableObject
|
||||
constrainedSizeRange:sizeRange
|
||||
return [[self alloc] initWithLayoutable:layoutable
|
||||
size:size
|
||||
position:CGPointNull
|
||||
position:position
|
||||
sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
size:(CGSize)size
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
|
||||
{
|
||||
return [self layoutWithLayoutableObject:layoutableObject
|
||||
constrainedSizeRange:sizeRange
|
||||
size:size
|
||||
position:CGPointNull
|
||||
sublayouts:nil];
|
||||
return [self layoutWithLayoutable:layoutable
|
||||
size:size
|
||||
position:CGPointNull
|
||||
sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
|
||||
constrainedSizeRange:(ASSizeRange)sizeRange
|
||||
size:(CGSize)size
|
||||
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
|
||||
+ (instancetype)layoutWithLayoutable:(id<ASLayoutable>)layoutable size:(CGSize)size
|
||||
{
|
||||
return [self layoutWithLayoutableObject:layoutableObject
|
||||
constrainedSizeRange:sizeRange
|
||||
size:size
|
||||
position:CGPointNull
|
||||
sublayouts:sublayouts];
|
||||
return [self layoutWithLayoutable:layoutable
|
||||
size:size
|
||||
position:CGPointNull
|
||||
sublayouts:nil];
|
||||
}
|
||||
|
||||
+ (instancetype)layoutWithLayout:(ASLayout *)layout position:(CGPoint)position
|
||||
{
|
||||
return [self layoutWithLayoutableObject:layout.layoutableObject
|
||||
constrainedSizeRange:layout.constrainedSizeRange
|
||||
size:layout.size
|
||||
position:position
|
||||
sublayouts:layout.sublayouts];
|
||||
return [self layoutWithLayoutable:layout.layoutable
|
||||
size:layout.size
|
||||
position:position
|
||||
sublayouts:layout.sublayouts];
|
||||
}
|
||||
|
||||
#pragma mark - Layout Flattening
|
||||
@@ -186,17 +165,14 @@ static inline NSString * descriptionIndents(NSUInteger indents)
|
||||
}
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:_layoutableObject
|
||||
constrainedSizeRange:_constrainedSizeRange
|
||||
size:_size
|
||||
sublayouts:flattenedSublayouts];
|
||||
return [ASLayout layoutWithLayoutable:_layoutable size:_size sublayouts:flattenedSublayouts];
|
||||
}
|
||||
|
||||
#pragma mark - Accessors
|
||||
|
||||
- (ASLayoutableType)type
|
||||
{
|
||||
return _layoutableObject.layoutableType;
|
||||
return _layoutable.layoutableType;
|
||||
}
|
||||
|
||||
- (CGRect)frame
|
||||
@@ -231,8 +207,8 @@ static inline NSString * descriptionIndents(NSUInteger indents)
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<<ASLayout: %p>, layoutable = %@, position = %@; size = %@; constrainedSizeRange = %@>",
|
||||
self, self.layoutableObject, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size), NSStringFromASSizeRange(self.constrainedSizeRange)];
|
||||
return [NSString stringWithFormat:@"<<ASLayout: %p>, layoutable = %@, position = %@; size = %@;>",
|
||||
self, self.layoutable, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size)/*, NSStringFromASSizeRange(self.constrainedSize)*/];
|
||||
}
|
||||
|
||||
- (NSString *)recursiveDescription
|
||||
@@ -253,3 +229,18 @@ static inline NSString * descriptionIndents(NSUInteger indents)
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
ASLayout *ASCalculateLayout(id<ASLayoutable> layoutable, const ASSizeRange sizeRange, const CGSize parentSize)
|
||||
{
|
||||
ASDisplayNodeCAssertNotNil(layoutable, @"Not valid layoutable passed in.");
|
||||
|
||||
return [layoutable layoutThatFits:sizeRange parentSize:parentSize];
|
||||
}
|
||||
|
||||
ASLayout *ASCalculateRootLayout(id<ASLayoutable> rootLayoutable, const ASSizeRange sizeRange)
|
||||
{
|
||||
ASLayout *layout = ASCalculateLayout(rootLayoutable, sizeRange, sizeRange.max);
|
||||
// Here could specific verfication happen
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** A layout spec is an immutable object that describes a layout, loosely inspired by React. */
|
||||
/**
|
||||
* A layout spec is an immutable object that describes a layout, loosely inspired by React.
|
||||
*/
|
||||
@interface ASLayoutSpec : NSObject <ASLayoutable>
|
||||
|
||||
/**
|
||||
@@ -88,6 +90,28 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* An ASLayoutSpec subclass that can wrap a ASLayoutable and calculates the layout of the child.
|
||||
*/
|
||||
@interface ASWrapperLayoutSpec : ASLayoutSpec
|
||||
|
||||
/*
|
||||
* Returns an ASWrapperLayoutSpec object with the given layoutable as child
|
||||
*/
|
||||
+ (instancetype)wrapperWithLayoutable:(id<ASLayoutable>)layoutable;
|
||||
|
||||
/*
|
||||
* Returns an ASWrapperLayoutSpec object initialized with the given layoutable as child
|
||||
*/
|
||||
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable NS_DESIGNATED_INITIALIZER;;
|
||||
|
||||
/*
|
||||
* Init not available for ASWrapperLayoutSpec
|
||||
*/
|
||||
- (instancetype)init __unavailable;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASLayoutSpec (Debugging) <ASLayoutableAsciiArtProtocol>
|
||||
/**
|
||||
* Used by other layout specs to create ascii art debug strings
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#import "ASLayoutSpec.h"
|
||||
|
||||
#import "ASAssert.h"
|
||||
#import "ASInternalHelpers.h"
|
||||
#import "ASEnvironmentInternal.h"
|
||||
|
||||
#import "ASLayout.h"
|
||||
@@ -24,25 +25,46 @@
|
||||
typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASChildMap;
|
||||
|
||||
@interface ASLayoutSpec() {
|
||||
ASEnvironmentState _environmentState;
|
||||
ASDN::RecursiveMutex __instanceLock__;
|
||||
ASLayoutableSize _size;
|
||||
ASEnvironmentState _environmentState;
|
||||
ASChildMap _children;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ASLayoutSpec
|
||||
|
||||
// these dynamic properties all defined in ASLayoutOptionsPrivate.m
|
||||
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis,
|
||||
alignSelf, ascender, descender, sizeRange, layoutPosition, layoutableType;
|
||||
// Dynamic properties for ASLayoutables
|
||||
@dynamic layoutableType, size;
|
||||
// Dynamic properties for sizing
|
||||
@dynamic width, height, minWidth, maxWidth, minHeight, maxHeight;
|
||||
// Dynamic properties for stack spec
|
||||
@dynamic spacingAfter, spacingBefore, flexGrow, flexShrink, flexBasis, alignSelf, ascender, descender;
|
||||
// Dynamic properties for static spec
|
||||
@dynamic layoutPosition;
|
||||
|
||||
@synthesize isFinalLayoutable = _isFinalLayoutable;
|
||||
|
||||
#pragma mark - Class
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
[super initialize];
|
||||
if (self != [ASLayoutSpec class]) {
|
||||
ASDisplayNodeAssert(!ASSubclassOverridesSelector([ASLayoutSpec class], self, @selector(measureWithSizeRange:)), @"Subclass %@ must not override measureWithSizeRange: method. Instead overwrite calculateLayoutThatFits:", NSStringFromClass(self));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Lifecycle
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if (!(self = [super init])) {
|
||||
return nil;
|
||||
}
|
||||
_isMutable = YES;
|
||||
_size = ASLayoutableSizeMake();
|
||||
_environmentState = ASEnvironmentStateMakeDefault();
|
||||
return self;
|
||||
}
|
||||
@@ -57,13 +79,54 @@ typedef std::map<unsigned long, id<ASLayoutable>, std::less<unsigned long>> ASCh
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Sizing
|
||||
|
||||
- (ASLayoutableSize)size
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
return _size;
|
||||
}
|
||||
|
||||
- (void)setSize:(ASLayoutableSize)size
|
||||
{
|
||||
ASDN::MutexLocker l(__instanceLock__);
|
||||
_size = size;
|
||||
}
|
||||
|
||||
ASLayoutableSizeForwarding
|
||||
ASLayoutableSizeHelperForwarding
|
||||
|
||||
|
||||
#pragma mark - Layout
|
||||
|
||||
// Deprecated
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
{
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:constrainedSize.min];
|
||||
return [self layoutThatFits:constrainedSize];
|
||||
}
|
||||
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
return [self layoutThatFits:constrainedSize parentSize:constrainedSize.max];
|
||||
}
|
||||
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize
|
||||
{
|
||||
return [self calculateLayoutThatFits:constrainedSize restrictedToSize:_size relativeToParentSize:parentSize];
|
||||
}
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize
|
||||
{
|
||||
const ASSizeRange resolvedRange = ASSizeRangeIntersect(constrainedSize, ASLayoutableSizeResolve(_size, parentSize));
|
||||
return [self calculateLayoutThatFits:resolvedRange];
|
||||
}
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
return [ASLayout layoutWithLayoutable:self size:constrainedSize.min];
|
||||
}
|
||||
|
||||
- (id<ASLayoutable>)finalLayoutable
|
||||
@@ -233,6 +296,37 @@ ASEnvironmentLayoutExtensibilityForwarding
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - ASWrapperLayoutSpec
|
||||
|
||||
@implementation ASWrapperLayoutSpec
|
||||
|
||||
+ (instancetype)wrapperWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
{
|
||||
return [[self alloc] initWithLayoutable:layoutable];
|
||||
}
|
||||
|
||||
- (instancetype)initWithLayoutable:(id<ASLayoutable>)layoutable
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.child = layoutable;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASLayout *sublayout = [self.child layoutThatFits:constrainedSize parentSize:constrainedSize.max];
|
||||
sublayout.position = CGPointZero;
|
||||
return [ASLayout layoutWithLayoutable:self size:sublayout.size sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - ASLayoutSpec (Debugging)
|
||||
|
||||
@implementation ASLayoutSpec (Debugging)
|
||||
|
||||
#pragma mark - ASLayoutableAsciiArtProtocol
|
||||
|
||||
@@ -65,28 +65,14 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
|
||||
- (void)validateLayout:(ASLayout *)layout
|
||||
{
|
||||
for (ASLayout *sublayout in layout.sublayouts) {
|
||||
id<ASLayoutable> layoutable = layout.layoutableObject;
|
||||
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutableObject;
|
||||
id<ASLayoutable> layoutable = layout.layoutable;
|
||||
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutable;
|
||||
|
||||
NSString *assertMessage = nil;
|
||||
Class stackContainerClass = [ASStaticLayoutSpec class];
|
||||
|
||||
// Check for default sizeRange and layoutPosition
|
||||
ASRelativeSizeRange sizeRange = sublayoutLayoutable.sizeRange;
|
||||
ASRelativeSizeRange zeroSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeZero);
|
||||
|
||||
// Currently setting the preferredFrameSize also updates the sizeRange. Create a size range based on the
|
||||
// preferredFrameSize and check it if it's the same as the current sizeRange to be sure it was not changed manually
|
||||
CGSize preferredFrameSize = CGSizeZero;
|
||||
if ([sublayoutLayoutable respondsToSelector:@selector(preferredFrameSize)]) {
|
||||
preferredFrameSize = [((ASDisplayNode *)sublayoutLayoutable) preferredFrameSize];
|
||||
}
|
||||
ASRelativeSizeRange preferredFrameSizeRange = ASRelativeSizeRangeMakeWithExactCGSize(preferredFrameSize);
|
||||
|
||||
if (ASRelativeSizeRangeEqualToRelativeSizeRange(sizeRange, zeroSizeRange) == NO &&
|
||||
ASRelativeSizeRangeEqualToRelativeSizeRange(sizeRange, preferredFrameSizeRange) == NO) {
|
||||
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(sizeRange), sublayoutLayoutable, stackContainerClass);
|
||||
} else if (!CGPointEqualToPoint(sublayoutLayoutable.layoutPosition, CGPointZero)) {
|
||||
// Check for default layoutPosition
|
||||
if (!CGPointEqualToPoint(sublayoutLayoutable.layoutPosition, CGPointZero)) {
|
||||
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(layoutPosition), sublayoutLayoutable, stackContainerClass);
|
||||
}
|
||||
|
||||
@@ -110,9 +96,9 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
|
||||
|
||||
- (void)validateLayout:(ASLayout *)layout
|
||||
{
|
||||
id<ASLayoutable> layoutable = layout.layoutableObject;
|
||||
id<ASLayoutable> layoutable = layout.layoutable;
|
||||
for (ASLayout *sublayout in layout.sublayouts) {
|
||||
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutableObject;
|
||||
id<ASLayoutable> sublayoutLayoutable = sublayout.layoutable;
|
||||
|
||||
NSString *assertMessage = nil;
|
||||
Class stackContainerClass = [ASStackLayoutSpec class];
|
||||
@@ -126,7 +112,7 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C
|
||||
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexGrow), sublayoutLayoutable, stackContainerClass);
|
||||
} else if (sublayoutLayoutable.flexShrink == YES) {
|
||||
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexShrink), sublayoutLayoutable, stackContainerClass);
|
||||
} else if (!ASRelativeDimensionEqualToRelativeDimension(sublayoutLayoutable.flexBasis, ASRelativeDimensionUnconstrained) ) {
|
||||
} else if (!ASDimensionEqualToDimension(sublayoutLayoutable.flexBasis, ASDimensionAuto) ) {
|
||||
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(flexBasis), sublayoutLayoutable, stackContainerClass);
|
||||
} else if (sublayoutLayoutable.alignSelf != ASStackLayoutAlignSelfAuto) {
|
||||
assertMessage = ASLayoutValidationWrappingAssertMessage(@selector(alignSelf), sublayoutLayoutable, stackContainerClass);
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutDefines.h>
|
||||
#import <AsyncDisplayKit/ASStackLayoutable.h>
|
||||
#import <AsyncDisplayKit/ASStaticLayoutable.h>
|
||||
@@ -21,6 +20,13 @@
|
||||
@class ASLayout;
|
||||
@class ASLayoutSpec;
|
||||
|
||||
/** A constant that indicates that the parent's size is not yet determined in a given dimension. */
|
||||
extern CGFloat const ASLayoutableParentDimensionUndefined;
|
||||
|
||||
/** A constant that indicates that the parent's size is not yet determined in either dimension. */
|
||||
extern CGSize const ASLayoutableParentSizeUndefined;
|
||||
|
||||
/** Type of ASLayoutable */
|
||||
typedef NS_ENUM(NSUInteger, ASLayoutableType) {
|
||||
ASLayoutableTypeLayoutSpec,
|
||||
ASLayoutableTypeDisplayNode
|
||||
@@ -49,24 +55,142 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
* @abstract Returns type of layoutable
|
||||
*/
|
||||
@property (nonatomic, readonly) ASLayoutableType layoutableType;
|
||||
@property (nonatomic, assign, readonly) ASLayoutableType layoutableType;
|
||||
|
||||
/**
|
||||
* @abstract Returns if the layoutable can be used to layout in an asynchronous way on a background thread.
|
||||
*/
|
||||
@property (nonatomic, readonly) BOOL canLayoutAsynchronous;
|
||||
@property (nonatomic, assign, readonly) BOOL canLayoutAsynchronous;
|
||||
|
||||
#pragma mark - Sizing
|
||||
|
||||
/**
|
||||
* @abstract Calculate a layout based on given size range.
|
||||
* @abstract The width property specifies the height of the content area of an ASLayoutable.
|
||||
* The minWidth and maxWidth properties override width.
|
||||
* Defaults to ASRelativeDimensionTypeAuto
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASDimension width;
|
||||
|
||||
/**
|
||||
* @abstract The height property specifies the height of the content area of an ASLayoutable
|
||||
* The minHeight and maxHeight properties override height.
|
||||
* Defaults to ASDimensionTypeAuto
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASDimension height;
|
||||
|
||||
/**
|
||||
* @abstract The minHeight property is used to set the minimum height of a given element. It prevents the used value
|
||||
* of the height property from becoming smaller than the value specified for minHeight.
|
||||
* The value of minHeight overrides both maxHeight and height.
|
||||
* Defaults to ASDimensionTypeAuto
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASDimension minHeight;
|
||||
|
||||
/**
|
||||
* @abstract The maxHeight property is used to set the maximum height of an element. It prevents the used value of the
|
||||
* height property from becoming larger than the value specified for maxHeight.
|
||||
* The value of maxHeight overrides height, but minHeight overrides maxHeight.
|
||||
* Defaults to ASDimensionTypeAuto
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASDimension maxHeight;
|
||||
|
||||
/**
|
||||
* @abstract The minWidth property is used to set the minimum width of a given element. It prevents the used value of
|
||||
* the width property from becoming smaller than the value specified for minWidth.
|
||||
* The value of minWidth overrides both maxWidth and width.
|
||||
* Defaults to ASDimensionTypeAuto
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASDimension minWidth;
|
||||
|
||||
/**
|
||||
* @abstract The maxWidth property is used to set the maximum width of a given element. It prevents the used value of
|
||||
* the width property from becoming larger than the value specified for maxWidth.
|
||||
* The value of maxWidth overrides width, but minWidth overrides maxWidth.
|
||||
* Defaults to ASDimensionTypeAuto
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASDimension maxWidth;
|
||||
|
||||
/**
|
||||
* @abstract Set max and width properties from given size
|
||||
*/
|
||||
- (void)setSizeWithCGSize:(CGSize)size;
|
||||
|
||||
/**
|
||||
* @abstract Set minHeight, maxHeight and minWidth, maxWidth properties from given size
|
||||
*/
|
||||
- (void)setExactSizeWithCGSize:(CGSize)size;
|
||||
|
||||
|
||||
#pragma mark - Calculate layout
|
||||
|
||||
/**
|
||||
* @abstract Asks the node to return a layout based on given size range.
|
||||
*
|
||||
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
|
||||
*
|
||||
* @return An ASLayout instance defining the layout of the receiver and its children.
|
||||
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
|
||||
*
|
||||
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
|
||||
* constraint and the result.
|
||||
*
|
||||
* @warning Subclasses must not override this; it caches results from -calculateLayoutThatFits:. Calling this method may
|
||||
* be expensive if result is not cached.
|
||||
*
|
||||
* @see [ASDisplayNode(Subclassing) calculateLayoutThatFits:]
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize;
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize;
|
||||
|
||||
/**
|
||||
* Call this on children layoutables to compute their layouts within your implementation of -calculateLayoutThatFits:.
|
||||
*
|
||||
* @warning You may not override this method. Override -calculateLayoutThatFits: instead.
|
||||
* @warning In almost all cases, prefer the use of ASCalculateLayout in ASLayout
|
||||
*
|
||||
* @param constrainedSize Specifies a minimum and maximum size. The receiver must choose a size that is in this range.
|
||||
* @param parentSize The parent node's size. If the parent component does not have a final size in a given dimension,
|
||||
* then it should be passed as ASLayoutableParentDimensionUndefined (for example, if the parent's width
|
||||
* depends on the child's size).
|
||||
*
|
||||
* @discussion Though this method does not set the bounds of the view, it does have side effects--caching both the
|
||||
* constraint and the result.
|
||||
*
|
||||
* @return An ASLayout instance defining the layout of the receiver (and its children, if the box layout model is used).
|
||||
*/
|
||||
- (ASLayout *)layoutThatFits:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize;
|
||||
|
||||
/**
|
||||
* Override this method to compute your layoutable's layout.
|
||||
*
|
||||
* @discussion Why do you need to override -calculateLayoutThatFits: instead of -layoutThatFits:parentSize:?
|
||||
* The base implementation of -layoutThatFits:parentSize: does the following for you:
|
||||
* 1. First, it uses the parentSize parameter to resolve the nodes's size (the one assigned to the size property).
|
||||
* 2. Then, it intersects the resolved size with the constrainedSize parameter. If the two don't intersect,
|
||||
* constrainedSize wins. This allows a component to always override its childrens' sizes when computing its layout.
|
||||
* (The analogy for UIView: you might return a certain size from -sizeThatFits:, but a parent view can always override
|
||||
* that size and set your frame to any size.)
|
||||
* 3. It caches it result for reuse
|
||||
*
|
||||
* @param constrainedSize A min and max size. This is computed as described in the description. The ASLayout you
|
||||
* return MUST have a size between these two sizes.
|
||||
*/
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize;
|
||||
|
||||
/**
|
||||
* In certain advanced cases, you may want to override this method. Overriding this method allows you to receive the
|
||||
* layoutable's size, parentSize, and constrained size. With these values you could calculate the final constrained size
|
||||
* and call -calculateLayoutThatFits: with the result.
|
||||
*
|
||||
* @warning Overriding this method should be done VERY rarely.
|
||||
*/
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize;
|
||||
|
||||
|
||||
|
||||
#pragma mark - Layout options from the Layoutable Protocols
|
||||
|
||||
|
||||
#pragma mark - ASStackLayoutable
|
||||
/**
|
||||
* @abstract Additional space to place before this object in the stacking direction.
|
||||
@@ -94,10 +218,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* @abstract Specifies the initial size in the stack dimension for this object.
|
||||
* Default to ASRelativeDimensionUnconstrained.
|
||||
* Default to ASRelativeDimensionAuto
|
||||
* Used when attached to a stack layout.
|
||||
*/
|
||||
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
|
||||
@property (nonatomic, readwrite) ASDimension flexBasis;
|
||||
|
||||
/**
|
||||
* @abstract Orientation of the object along cross axis, overriding alignItems
|
||||
@@ -115,15 +239,28 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, readwrite) CGFloat descender;
|
||||
|
||||
#pragma mark - ASStaticLayoutable
|
||||
/**
|
||||
If specified, the child's size is restricted according to this size. Fractions are resolved relative to the static layout spec.
|
||||
*/
|
||||
@property (nonatomic, assign) ASRelativeSizeRange sizeRange;
|
||||
|
||||
/** The position of this object within its parent spec. */
|
||||
#pragma mark - ASStaticLayoutable
|
||||
|
||||
/**
|
||||
* @abstract The position of this object within its parent spec.
|
||||
*/
|
||||
@property (nonatomic, assign) CGPoint layoutPosition;
|
||||
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
/**
|
||||
* @abstract Calculate a layout based on given size range.
|
||||
*
|
||||
* @param constrainedSize The minimum and maximum sizes the receiver should fit in.
|
||||
*
|
||||
* @return An ASLayout instance defining the layout of the receiver and its children.
|
||||
*
|
||||
* @deprecated Deprecated in version 2.0: Use ASCalculateRootLayout or ASCalculateLayout instead
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize ASDISPLAYNODE_DEPRECATED;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#import <map>
|
||||
|
||||
CGFloat const ASLayoutableParentDimensionUndefined = NAN;
|
||||
CGSize const ASLayoutableParentSizeUndefined = {ASLayoutableParentDimensionUndefined, ASLayoutableParentDimensionUndefined};
|
||||
|
||||
int32_t const ASLayoutableContextInvalidTransitionID = 0;
|
||||
int32_t const ASLayoutableContextDefaultTransitionID = ASLayoutableContextInvalidTransitionID + 1;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ASDimension.h"
|
||||
|
||||
@class ASLayoutSpec;
|
||||
@protocol ASLayoutable;
|
||||
@@ -40,6 +40,11 @@ extern void ASLayoutableClearCurrentContext();
|
||||
*/
|
||||
@protocol ASLayoutablePrivate <NSObject>
|
||||
|
||||
/**
|
||||
* @abstract A size constraint that should apply to this ASLayoutable.
|
||||
*/
|
||||
@property (nonatomic, assign, readwrite) ASLayoutableSize size;
|
||||
|
||||
/**
|
||||
* @abstract This method can be used to give the user a chance to wrap an ASLayoutable in an ASLayoutSpec
|
||||
* just before it is added to a parent ASLayoutSpec. For example, if you wanted an ASTextNode that was always
|
||||
@@ -61,9 +66,7 @@ extern void ASLayoutableClearCurrentContext();
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark - ASLayoutOptionsForwarding
|
||||
|
||||
#pragma mark - ASLayoutableForwarding
|
||||
/**
|
||||
* Both an ASDisplayNode and an ASLayoutSpec conform to ASLayoutable. There are several properties
|
||||
* in ASLayoutable that are used when a node or spec is used in a layout spec.
|
||||
@@ -76,6 +79,103 @@ extern void ASLayoutableClearCurrentContext();
|
||||
* layoutSpec may require.
|
||||
*/
|
||||
|
||||
#pragma mark - ASLayoutableSizeForwarding
|
||||
|
||||
#define ASLayoutableSizeForwarding \
|
||||
\
|
||||
- (ASDimension)width\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
return _size.width;\
|
||||
}\
|
||||
\
|
||||
- (void)setWidth:(ASDimension)width\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
_size.width = width;\
|
||||
}\
|
||||
\
|
||||
- (ASDimension)height\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
return _size.height;\
|
||||
}\
|
||||
\
|
||||
- (void)setHeight:(ASDimension)height\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
_size.height = height;\
|
||||
}\
|
||||
\
|
||||
- (ASDimension)minWidth\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
return _size.minWidth;\
|
||||
}\
|
||||
\
|
||||
- (void)setMinWidth:(ASDimension)minWidth\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
_size.minWidth = minWidth;\
|
||||
}\
|
||||
\
|
||||
- (ASDimension)maxWidth\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
return _size.maxWidth;\
|
||||
}\
|
||||
\
|
||||
- (void)setMaxWidth:(ASDimension)maxWidth\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
_size.maxWidth = maxWidth;\
|
||||
}\
|
||||
\
|
||||
- (ASDimension)minHeight\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
return _size.minHeight;\
|
||||
}\
|
||||
\
|
||||
- (void)setMinHeight:(ASDimension)minHeight\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
_size.minHeight = minHeight;\
|
||||
}\
|
||||
\
|
||||
- (ASDimension)maxHeight\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
return _size.maxHeight;\
|
||||
}\
|
||||
\
|
||||
- (void)setMaxHeight:(ASDimension)maxHeight\
|
||||
{\
|
||||
ASDN::MutexLocker l(__instanceLock__);\
|
||||
_size.maxHeight = maxHeight;\
|
||||
}\
|
||||
|
||||
|
||||
#pragma mark - ASLayoutableSizeHelperForwarding
|
||||
|
||||
#define ASLayoutableSizeHelperForwarding \
|
||||
- (void)setSizeWithCGSize:(CGSize)size\
|
||||
{\
|
||||
self.width = ASDimensionMakeWithPoints(size.width);\
|
||||
self.height = ASDimensionMakeWithPoints(size.height);\
|
||||
}\
|
||||
\
|
||||
- (void)setExactSizeWithCGSize:(CGSize)size\
|
||||
{\
|
||||
self.minWidth = ASDimensionMakeWithPoints(size.width);\
|
||||
self.minHeight = ASDimensionMakeWithPoints(size.height);\
|
||||
self.maxWidth = ASDimensionMakeWithPoints(size.width);\
|
||||
self.maxHeight = ASDimensionMakeWithPoints(size.height);\
|
||||
}\
|
||||
|
||||
|
||||
#pragma mark - ASLayoutOptionsForwarding
|
||||
|
||||
#define ASEnvironmentLayoutOptionsForwarding \
|
||||
- (void)propagateUpLayoutOptionsState\
|
||||
{\
|
||||
@@ -132,12 +232,12 @@ extern void ASLayoutableClearCurrentContext();
|
||||
[self propagateUpLayoutOptionsState];\
|
||||
}\
|
||||
\
|
||||
- (ASRelativeDimension)flexBasis\
|
||||
- (ASDimension)flexBasis\
|
||||
{\
|
||||
return _environmentState.layoutOptionsState.flexBasis;\
|
||||
}\
|
||||
\
|
||||
- (void)setFlexBasis:(ASRelativeDimension)flexBasis\
|
||||
- (void)setFlexBasis:(ASDimension)flexBasis\
|
||||
{\
|
||||
_environmentState.layoutOptionsState.flexBasis = flexBasis;\
|
||||
[self propagateUpLayoutOptionsState];\
|
||||
@@ -176,17 +276,6 @@ extern void ASLayoutableClearCurrentContext();
|
||||
[self propagateUpLayoutOptionsState];\
|
||||
}\
|
||||
\
|
||||
- (ASRelativeSizeRange)sizeRange\
|
||||
{\
|
||||
return _environmentState.layoutOptionsState.sizeRange;\
|
||||
}\
|
||||
\
|
||||
- (void)setSizeRange:(ASRelativeSizeRange)sizeRange\
|
||||
{\
|
||||
_environmentState.layoutOptionsState.sizeRange = sizeRange;\
|
||||
[self propagateUpLayoutOptionsState];\
|
||||
}\
|
||||
\
|
||||
- (CGPoint)layoutPosition\
|
||||
{\
|
||||
return _environmentState.layoutOptionsState.layoutPosition;\
|
||||
|
||||
@@ -47,21 +47,21 @@ static NSUInteger const kOverlayChildIndex = 1;
|
||||
/**
|
||||
First layout the contents, then fit the overlay on top of it.
|
||||
*/
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
restrictedToSize:(ASLayoutableSize)size
|
||||
relativeToParentSize:(CGSize)parentSize
|
||||
{
|
||||
ASLayout *contentsLayout = [self.child measureWithSizeRange:constrainedSize];
|
||||
ASLayout *contentsLayout = [self.child layoutThatFits:constrainedSize parentSize:parentSize];
|
||||
contentsLayout.position = CGPointZero;
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithObject:contentsLayout];
|
||||
if (self.overlay) {
|
||||
ASLayout *overlayLayout = [self.overlay measureWithSizeRange:{contentsLayout.size, contentsLayout.size}];
|
||||
ASLayout *overlayLayout = [self.overlay layoutThatFits:ASSizeRangeMake(contentsLayout.size)
|
||||
parentSize:contentsLayout.size];
|
||||
overlayLayout.position = CGPointZero;
|
||||
[sublayouts addObject:overlayLayout];
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:contentsLayout.size
|
||||
sublayouts:sublayouts];
|
||||
return [ASLayout layoutWithLayoutable:self size:contentsLayout.size sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -47,16 +47,18 @@
|
||||
_ratio = ratio;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
std::vector<CGSize> sizeOptions;
|
||||
if (!isinf(constrainedSize.max.width)) {
|
||||
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
|
||||
if (!isinf(constrainedSize.max.width) && ASPointsAreValidForLayout(constrainedSize.max.width)) {
|
||||
sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
|
||||
constrainedSize.max.width,
|
||||
ASFloorPixelValue(_ratio * constrainedSize.max.width)
|
||||
}));
|
||||
}
|
||||
if (!isinf(constrainedSize.max.height)) {
|
||||
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
|
||||
if (!isinf(constrainedSize.max.height) && ASPointsAreValidForLayout(constrainedSize.max.width)) {
|
||||
sizeOptions.push_back(ASSizeRangeClamp(constrainedSize, {
|
||||
ASFloorPixelValue(constrainedSize.max.height / _ratio),
|
||||
constrainedSize.max.height
|
||||
@@ -70,12 +72,10 @@
|
||||
|
||||
// If there is no max size in *either* dimension, we can't apply the ratio, so just pass our size range through.
|
||||
const ASSizeRange childRange = (bestSize == sizeOptions.end()) ? constrainedSize : ASSizeRangeMake(*bestSize, *bestSize);
|
||||
ASLayout *sublayout = [self.child measureWithSizeRange:childRange];
|
||||
const CGSize parentSize = (bestSize == sizeOptions.end()) ? ASLayoutableParentSizeUndefined : *bestSize;
|
||||
ASLayout *sublayout = [self.child layoutThatFits:childRange parentSize:parentSize];
|
||||
sublayout.position = CGPointZero;
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:sublayout.size
|
||||
sublayouts:@[sublayout]];
|
||||
return [ASLayout layoutWithLayoutable:self size:sublayout.size sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -52,11 +52,15 @@
|
||||
_sizingOption = sizingOption;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
// If we have a finite size in any direction, pass this so that the child can
|
||||
// resolve percentages against it. Otherwise pass ASLayoutableParentDimensionUndefined
|
||||
// as the size will depend on the content
|
||||
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
|
||||
CGSize size = {
|
||||
constrainedSize.max.width,
|
||||
constrainedSize.max.height
|
||||
isinf(constrainedSize.max.width) || !ASPointsAreValidForLayout(constrainedSize.max.width) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.width,
|
||||
isinf(constrainedSize.max.height) || !ASPointsAreValidForLayout(constrainedSize.max.height) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.height
|
||||
};
|
||||
|
||||
BOOL reduceWidth = (_horizontalPosition & ASRelativeLayoutSpecPositionCenter) != 0 ||
|
||||
@@ -70,7 +74,8 @@
|
||||
reduceWidth ? 0 : constrainedSize.min.width,
|
||||
reduceHeight ? 0 : constrainedSize.min.height,
|
||||
};
|
||||
ASLayout *sublayout = [self.child measureWithSizeRange:ASSizeRangeMake(minChildSize, constrainedSize.max)];
|
||||
|
||||
ASLayout *sublayout = [self.child layoutThatFits:ASSizeRangeMake(minChildSize, constrainedSize.max) parentSize:size];
|
||||
|
||||
// If we have an undetermined height or width, use the child size to define the layout
|
||||
// size
|
||||
@@ -94,10 +99,7 @@
|
||||
ASRoundPixelValue((size.height - sublayout.size.height) * yPosition)
|
||||
};
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:size
|
||||
sublayouts:@[sublayout]];
|
||||
return [ASLayout layoutWithLayoutable:self size:size sublayouts:@[sublayout]];
|
||||
}
|
||||
|
||||
- (CGFloat)proportionOfAxisForAxisPosition:(ASRelativeLayoutSpecPosition)position
|
||||
|
||||
@@ -8,68 +8,4 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
#import <AsyncDisplayKit/ASDimension.h>
|
||||
|
||||
/**
|
||||
Expresses a size with relative dimensions.
|
||||
Used by ASStaticLayoutSpec.
|
||||
*/
|
||||
typedef struct {
|
||||
ASRelativeDimension width;
|
||||
ASRelativeDimension height;
|
||||
} ASRelativeSize;
|
||||
|
||||
/**
|
||||
Expresses an inclusive range of relative sizes. Used to provide additional constraint to layout.
|
||||
Used by ASStaticLayoutSpec.
|
||||
*/
|
||||
typedef struct {
|
||||
ASRelativeSize min;
|
||||
ASRelativeSize max;
|
||||
} ASRelativeSizeRange;
|
||||
|
||||
extern ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained;
|
||||
|
||||
ASDISPLAYNODE_EXTERN_C_BEGIN
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
#pragma mark - ASRelativeSize
|
||||
|
||||
extern ASRelativeSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height);
|
||||
|
||||
/** Convenience constructor to provide size in points. */
|
||||
extern ASRelativeSize ASRelativeSizeMakeWithCGSize(CGSize size);
|
||||
|
||||
/** Convenience constructor to provide size as a fraction. */
|
||||
extern ASRelativeSize ASRelativeSizeMakeWithFraction(CGFloat fraction);
|
||||
|
||||
/** Resolve this relative size relative to a parent size. */
|
||||
extern CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize);
|
||||
|
||||
extern BOOL ASRelativeSizeEqualToRelativeSize(ASRelativeSize lhs, ASRelativeSize rhs);
|
||||
|
||||
extern NSString *NSStringFromASRelativeSize(ASRelativeSize size);
|
||||
|
||||
#pragma mark - ASRelativeSizeRange
|
||||
|
||||
extern ASRelativeSizeRange ASRelativeSizeRangeMake(ASRelativeSize min, ASRelativeSize max);
|
||||
|
||||
#pragma mark Convenience constructors to provide an exact size (min == max).
|
||||
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSize exact);
|
||||
|
||||
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact);
|
||||
|
||||
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction);
|
||||
|
||||
extern ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth,
|
||||
ASRelativeDimension exactHeight);
|
||||
|
||||
extern BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs);
|
||||
|
||||
/** Provided a parent size, compute final dimensions for this RelativeSizeRange to arrive at a SizeRange. */
|
||||
extern ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange, CGSize parentSize);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
ASDISPLAYNODE_EXTERN_C_END
|
||||
// TODO: layout: Remove file
|
||||
|
||||
@@ -8,84 +8,4 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import "ASRelativeSize.h"
|
||||
|
||||
ASRelativeSizeRange const ASRelativeSizeRangeUnconstrained = {};
|
||||
|
||||
#pragma mark - ASRelativeSize
|
||||
|
||||
ASRelativeSize ASRelativeSizeMake(ASRelativeDimension width, ASRelativeDimension height)
|
||||
{
|
||||
ASRelativeSize size; size.width = width; size.height = height; return size;
|
||||
}
|
||||
|
||||
ASRelativeSize ASRelativeSizeMakeWithCGSize(CGSize size)
|
||||
{
|
||||
return ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(size.width),
|
||||
ASRelativeDimensionMakeWithPoints(size.height));
|
||||
}
|
||||
|
||||
ASRelativeSize ASRelativeSizeMakeWithFraction(CGFloat fraction)
|
||||
{
|
||||
return ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(fraction),
|
||||
ASRelativeDimensionMakeWithFraction(fraction));
|
||||
}
|
||||
|
||||
CGSize ASRelativeSizeResolveSize(ASRelativeSize relativeSize, CGSize parentSize)
|
||||
{
|
||||
return CGSizeMake(ASRelativeDimensionResolve(relativeSize.width, parentSize.width),
|
||||
ASRelativeDimensionResolve(relativeSize.height, parentSize.height));
|
||||
}
|
||||
|
||||
BOOL ASRelativeSizeEqualToRelativeSize(ASRelativeSize lhs, ASRelativeSize rhs)
|
||||
{
|
||||
return ASRelativeDimensionEqualToRelativeDimension(lhs.width, rhs.width)
|
||||
&& ASRelativeDimensionEqualToRelativeDimension(lhs.height, rhs.height);
|
||||
}
|
||||
|
||||
NSString *NSStringFromASRelativeSize(ASRelativeSize size)
|
||||
{
|
||||
return [NSString stringWithFormat:@"{%@, %@}",
|
||||
NSStringFromASRelativeDimension(size.width),
|
||||
NSStringFromASRelativeDimension(size.height)];
|
||||
}
|
||||
|
||||
#pragma mark - ASRelativeSizeRange
|
||||
|
||||
ASRelativeSizeRange ASRelativeSizeRangeMake(ASRelativeSize min, ASRelativeSize max)
|
||||
{
|
||||
ASRelativeSizeRange sizeRange; sizeRange.min = min; sizeRange.max = max; return sizeRange;
|
||||
}
|
||||
|
||||
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSize exact)
|
||||
{
|
||||
return ASRelativeSizeRangeMake(exact, exact);
|
||||
}
|
||||
|
||||
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactCGSize(CGSize exact)
|
||||
{
|
||||
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithCGSize(exact));
|
||||
}
|
||||
|
||||
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactFraction(CGFloat fraction)
|
||||
{
|
||||
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMakeWithFraction(fraction));
|
||||
}
|
||||
|
||||
ASRelativeSizeRange ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimension exactWidth,
|
||||
ASRelativeDimension exactHeight)
|
||||
{
|
||||
return ASRelativeSizeRangeMakeWithExactRelativeSize(ASRelativeSizeMake(exactWidth, exactHeight));
|
||||
}
|
||||
|
||||
BOOL ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRange lhs, ASRelativeSizeRange rhs)
|
||||
{
|
||||
return ASRelativeSizeEqualToRelativeSize(lhs.min, rhs.min) && ASRelativeSizeEqualToRelativeSize(lhs.max, rhs.max);
|
||||
}
|
||||
|
||||
ASSizeRange ASRelativeSizeRangeResolve(ASRelativeSizeRange relativeSizeRange,
|
||||
CGSize parentSize)
|
||||
{
|
||||
return ASSizeRangeMake(ASRelativeSizeResolveSize(relativeSizeRange.min, parentSize),
|
||||
ASRelativeSizeResolveSize(relativeSizeRange.max, parentSize));
|
||||
}
|
||||
// TODO: layout: Remove file
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
_baselineRelativeArrangement = baselineRelativeArrangement;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
std::vector<id<ASLayoutable>> stackChildren;
|
||||
for (id<ASLayoutable> child in self.children) {
|
||||
@@ -126,9 +126,7 @@
|
||||
}
|
||||
|
||||
if (stackChildren.empty()) {
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:constrainedSize.min];
|
||||
return [ASLayout layoutWithLayoutable:self size:constrainedSize.min];
|
||||
}
|
||||
|
||||
ASStackLayoutSpecStyle style = {.direction = _direction, .spacing = _spacing, .justifyContent = _justifyContent, .alignItems = _alignItems, .baselineRelativeArrangement = _baselineRelativeArrangement};
|
||||
@@ -161,10 +159,7 @@
|
||||
sublayouts = [NSArray arrayWithObjects:&positionedLayout.sublayouts[0] count:positionedLayout.sublayouts.size()];
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:ASSizeRangeClamp(constrainedSize, finalSize)
|
||||
sublayouts:sublayouts];
|
||||
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, finalSize) sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
- (void)resolveHorizontalAlignment
|
||||
|
||||
@@ -44,10 +44,10 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* @abstract Specifies the initial size in the stack dimension for this object.
|
||||
* Default to ASRelativeDimensionUnconstrained.
|
||||
* Default to ASDimensionAuto
|
||||
* Used when attached to a stack layout.
|
||||
*/
|
||||
@property (nonatomic, readwrite) ASRelativeDimension flexBasis;
|
||||
@property (nonatomic, readwrite) ASDimension flexBasis;
|
||||
|
||||
/**
|
||||
* @abstract Orientation of the object along cross axis, overriding alignItems
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASLayoutSpec.h>
|
||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
|
||||
@@ -34,42 +34,46 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGSize maxConstrainedSize = CGSizeMake(constrainedSize.max.width, constrainedSize.max.height);
|
||||
// TODO: layout: isValidForLayout() call should not be necessary if INFINITY is used
|
||||
CGSize size = {
|
||||
(isinf(constrainedSize.max.width) || !ASPointsAreValidForLayout(constrainedSize.max.width)) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.width,
|
||||
(isinf(constrainedSize.max.height) || !ASPointsAreValidForLayout(constrainedSize.max.height)) ? ASLayoutableParentDimensionUndefined : constrainedSize.max.height
|
||||
};
|
||||
|
||||
NSArray *children = self.children;
|
||||
NSMutableArray *sublayouts = [NSMutableArray arrayWithCapacity:children.count];
|
||||
|
||||
for (id<ASLayoutable> child in children) {
|
||||
CGPoint layoutPosition = child.layoutPosition;
|
||||
CGSize autoMaxSize = CGSizeMake(maxConstrainedSize.width - layoutPosition.x,
|
||||
maxConstrainedSize.height - layoutPosition.y);
|
||||
CGSize autoMaxSize = {
|
||||
constrainedSize.max.width - layoutPosition.x,
|
||||
constrainedSize.max.height - layoutPosition.y
|
||||
};
|
||||
|
||||
const ASSizeRange childConstraint = ASLayoutableSizeResolveAutoSize(child.size, size, {{0,0}, autoMaxSize});
|
||||
|
||||
ASRelativeSizeRange childSizeRange = child.sizeRange;
|
||||
BOOL childIsUnconstrained = ASRelativeSizeRangeEqualToRelativeSizeRange(ASRelativeSizeRangeUnconstrained, childSizeRange);
|
||||
ASSizeRange childConstraint = childIsUnconstrained ? ASSizeRangeMake({0, 0}, autoMaxSize)
|
||||
: ASRelativeSizeRangeResolve(childSizeRange, maxConstrainedSize);
|
||||
|
||||
ASLayout *sublayout = [child measureWithSizeRange:childConstraint];
|
||||
ASLayout *sublayout = [child layoutThatFits:childConstraint parentSize:size];
|
||||
sublayout.position = layoutPosition;
|
||||
[sublayouts addObject:sublayout];
|
||||
}
|
||||
|
||||
CGSize size = CGSizeMake(constrainedSize.min.width, constrainedSize.min.height);
|
||||
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
CGPoint sublayoutPosition = sublayout.position;
|
||||
CGSize sublayoutSize = sublayout.size;
|
||||
|
||||
size.width = MAX(size.width, sublayoutPosition.x + sublayoutSize.width);
|
||||
size.height = MAX(size.height, sublayoutPosition.y + sublayoutSize.height);
|
||||
if (isnan(size.width)) {
|
||||
size.width = constrainedSize.min.width;
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
size.width = MAX(size.width, sublayout.position.x + sublayout.size.width);
|
||||
}
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:ASSizeRangeClamp(constrainedSize, size)
|
||||
sublayouts:sublayouts];
|
||||
|
||||
if (isnan(size.height)) {
|
||||
size.height = constrainedSize.min.height;
|
||||
for (ASLayout *sublayout in sublayouts) {
|
||||
size.height = MAX(size.height, sublayout.position.y + sublayout.size.height);
|
||||
}
|
||||
}
|
||||
|
||||
return [ASLayout layoutWithLayoutable:self size:ASSizeRangeClamp(constrainedSize, size) sublayouts:sublayouts];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
|
||||
#import <AsyncDisplayKit/ASRelativeSize.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
@@ -18,11 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol ASStaticLayoutable
|
||||
|
||||
/**
|
||||
If specified, the child's size is restricted according to this size. Fractions are resolved relative to the static layout spec.
|
||||
* @abstract The position of this object within its parent spec.
|
||||
*/
|
||||
@property (nonatomic, assign) ASRelativeSizeRange sizeRange;
|
||||
|
||||
/** The position of this object within its parent spec. */
|
||||
@property (nonatomic, assign) CGPoint layoutPosition;
|
||||
|
||||
@end
|
||||
|
||||
@@ -43,8 +43,6 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
|
||||
ASDisplayNodeMethodOverrideLayoutSpecThatFits = 1 << 4
|
||||
};
|
||||
|
||||
@class _ASDisplayNodePosition;
|
||||
|
||||
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayScheduledNodesNotification;
|
||||
FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBeforeTimestamp;
|
||||
|
||||
@@ -104,6 +102,9 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
||||
|
||||
@protected
|
||||
ASDisplayNode * __weak _supernode;
|
||||
|
||||
ASLayoutableSize _size;
|
||||
CGSize _preferredFrameSize;
|
||||
|
||||
ASSentinel *_displaySentinel;
|
||||
|
||||
@@ -114,21 +115,20 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
||||
CGFloat _contentsScaleForDisplay;
|
||||
|
||||
ASEnvironmentState _environmentState;
|
||||
ASLayout *_calculatedLayout;
|
||||
|
||||
|
||||
UIEdgeInsets _hitTestSlop;
|
||||
NSMutableArray *_subnodes;
|
||||
|
||||
// Main thread only
|
||||
_ASTransitionContext *_pendingLayoutTransitionContext;
|
||||
BOOL _automaticallyManagesSubnodes;
|
||||
_ASTransitionContext *_pendingLayoutTransitionContext;
|
||||
NSTimeInterval _defaultLayoutTransitionDuration;
|
||||
NSTimeInterval _defaultLayoutTransitionDelay;
|
||||
UIViewAnimationOptions _defaultLayoutTransitionOptions;
|
||||
|
||||
int32_t _pendingTransitionID;
|
||||
ASLayoutTransition *_pendingLayoutTransition;
|
||||
std::shared_ptr<ASDisplayNodeLayout> _calculatedDisplayNodeLayout;
|
||||
|
||||
ASDisplayNodeViewBlock _viewBlock;
|
||||
ASDisplayNodeLayerBlock _layerBlock;
|
||||
@@ -184,7 +184,6 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
|
||||
|
||||
// Swizzle to extend the builtin functionality with custom logic
|
||||
- (BOOL)__shouldLoadViewOrLayer;
|
||||
- (BOOL)__shouldSize;
|
||||
|
||||
/**
|
||||
Invoked before a call to setNeedsLayout to the underlying view
|
||||
|
||||
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) {
|
||||
parentLayoutOptionsState.flexGrow = layoutOptionsState.flexGrow;
|
||||
}
|
||||
if (ASRelativeDimensionEqualToRelativeDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) {
|
||||
if (ASDimensionEqualToDimension(parentLayoutOptionsState.flexBasis, defaultState.flexBasis)) {
|
||||
parentLayoutOptionsState.flexBasis = layoutOptionsState.flexBasis;
|
||||
}
|
||||
if (parentLayoutOptionsState.alignSelf == defaultState.alignSelf) {
|
||||
@@ -154,10 +154,6 @@ ASEnvironmentState ASEnvironmentMergeObjectAndState(ASEnvironmentState environme
|
||||
parentLayoutOptionsState.ascender = layoutOptionsState.ascender;
|
||||
}
|
||||
|
||||
if (ASRelativeSizeRangeEqualToRelativeSizeRange(parentLayoutOptionsState.sizeRange, defaultState.sizeRange)) {
|
||||
// For now it is unclear if we should be up-propagating sizeRange or layoutPosition.
|
||||
// parentLayoutOptionsState.sizeRange = layoutOptionsState.sizeRange;
|
||||
}
|
||||
if (CGPointEqualToPoint(parentLayoutOptionsState.layoutPosition, defaultState.layoutPosition)) {
|
||||
// For now it is unclear if we should be up-propagating sizeRange or layoutPosition.
|
||||
// parentLayoutOptionsState.layoutPosition = layoutOptionsState.layoutPosition;
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreGraphics/CGBase.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASBaseDefines.h>
|
||||
|
||||
|
||||
@@ -12,9 +12,13 @@
|
||||
|
||||
#import "ASDimension.h"
|
||||
#import "_ASTransitionContext.h"
|
||||
#import "ASDisplayNodeLayout.h"
|
||||
|
||||
#import <memory>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ASDisplayNode;
|
||||
@class ASLayout;
|
||||
|
||||
@interface ASLayoutTransition : NSObject <_ASTransitionContextLayoutDelegate>
|
||||
|
||||
@@ -26,12 +30,12 @@
|
||||
/**
|
||||
* Previous layout to transition from
|
||||
*/
|
||||
@property (nonatomic, readonly, strong) ASLayout *previousLayout;
|
||||
@property (nonatomic, readonly, assign) std::shared_ptr<ASDisplayNodeLayout> previousLayout;
|
||||
|
||||
/**
|
||||
* Pending layout to transition to
|
||||
*/
|
||||
@property (nonatomic, readonly, strong) ASLayout *pendingLayout;
|
||||
@property (nonatomic, readonly, assign) std::shared_ptr<ASDisplayNodeLayout> pendingLayout;
|
||||
|
||||
/**
|
||||
* Returns if the layout transition needs to happen synchronously
|
||||
@@ -41,8 +45,9 @@
|
||||
/**
|
||||
* Returns a newly initialized layout transition
|
||||
*/
|
||||
- (instancetype)initWithNode:(ASDisplayNode *)node pendingLayout:(ASLayout *)pendingLayout previousLayout:(ASLayout *)previousLayout NS_DESIGNATED_INITIALIZER;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (instancetype)initWithNode:(ASDisplayNode *)node
|
||||
pendingLayout:(std::shared_ptr<ASDisplayNodeLayout>)pendingLayout
|
||||
previousLayout:(std::shared_ptr<ASDisplayNodeLayout>)previousLayout NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
/**
|
||||
* Insert and remove subnodes that where added or removed between the previousLayout and the pendingLayout
|
||||
@@ -60,3 +65,11 @@
|
||||
- (void)applySubnodeRemovals;
|
||||
|
||||
@end
|
||||
|
||||
@interface ASLayoutTransition (Unavailable)
|
||||
|
||||
- (instancetype)init __unavailable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@@ -34,7 +34,7 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
|
||||
layout = queue.front();
|
||||
queue.pop();
|
||||
|
||||
if (layout.layoutableObject.canLayoutAsynchronous == NO) {
|
||||
if (layout.layoutable.canLayoutAsynchronous == NO) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
|
||||
}
|
||||
|
||||
- (instancetype)initWithNode:(ASDisplayNode *)node
|
||||
pendingLayout:(ASLayout *)pendingLayout
|
||||
previousLayout:(ASLayout *)previousLayout
|
||||
pendingLayout:(std::shared_ptr<ASDisplayNodeLayout>)pendingLayout
|
||||
previousLayout:(std::shared_ptr<ASDisplayNodeLayout>)previousLayout
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
@@ -72,10 +72,16 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
ASDisplayNodeAssert(NO, @"Use the designated initializer");
|
||||
return [self init];
|
||||
}
|
||||
|
||||
- (BOOL)isSynchronous
|
||||
{
|
||||
ASDN::MutexSharedLocker l(__instanceLock__);
|
||||
return !ASLayoutCanTransitionAsynchronous(_pendingLayout);
|
||||
return !ASLayoutCanTransitionAsynchronous(_pendingLayout->layout);
|
||||
}
|
||||
|
||||
- (void)commitTransition
|
||||
@@ -112,23 +118,27 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
|
||||
if (_calculatedSubnodeOperations) {
|
||||
return;
|
||||
}
|
||||
if (_previousLayout) {
|
||||
|
||||
ASLayout *previousLayout = _previousLayout->layout;
|
||||
ASLayout *pendingLayout = _pendingLayout->layout;
|
||||
|
||||
if (previousLayout) {
|
||||
NSIndexSet *insertions, *deletions;
|
||||
[_previousLayout.sublayouts asdk_diffWithArray:_pendingLayout.sublayouts
|
||||
insertions:&insertions
|
||||
deletions:&deletions
|
||||
compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) {
|
||||
return ASObjectIsEqual(lhs.layoutableObject, rhs.layoutableObject);
|
||||
}];
|
||||
findNodesInLayoutAtIndexes(_pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions);
|
||||
findNodesInLayoutAtIndexesWithFilteredNodes(_previousLayout,
|
||||
deletions,
|
||||
_insertedSubnodes,
|
||||
&_removedSubnodes,
|
||||
&_removedSubnodePositions);
|
||||
[previousLayout.sublayouts asdk_diffWithArray:pendingLayout.sublayouts
|
||||
insertions:&insertions
|
||||
deletions:&deletions
|
||||
compareBlock:^BOOL(ASLayout *lhs, ASLayout *rhs) {
|
||||
return ASObjectIsEqual(lhs.layoutable, rhs.layoutable);
|
||||
}];
|
||||
findNodesInLayoutAtIndexes(pendingLayout, insertions, &_insertedSubnodes, &_insertedSubnodePositions);
|
||||
findNodesInLayoutAtIndexesWithFilteredNodes(previousLayout,
|
||||
deletions,
|
||||
_insertedSubnodes,
|
||||
&_removedSubnodes,
|
||||
&_removedSubnodePositions);
|
||||
} else {
|
||||
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [_pendingLayout.sublayouts count])];
|
||||
findNodesInLayoutAtIndexes(_pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions);
|
||||
NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, [pendingLayout.sublayouts count])];
|
||||
findNodesInLayoutAtIndexes(pendingLayout, indexes, &_insertedSubnodes, &_insertedSubnodePositions);
|
||||
_removedSubnodes = nil;
|
||||
}
|
||||
_calculatedSubnodeOperations = YES;
|
||||
@@ -160,9 +170,9 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
|
||||
{
|
||||
ASDN::MutexSharedLocker l(__instanceLock__);
|
||||
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
|
||||
return _previousLayout;
|
||||
return _previousLayout->layout;
|
||||
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
|
||||
return _pendingLayout;
|
||||
return _pendingLayout->layout;
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
@@ -172,9 +182,9 @@ static inline BOOL ASLayoutCanTransitionAsynchronous(ASLayout *layout) {
|
||||
{
|
||||
ASDN::MutexSharedLocker l(__instanceLock__);
|
||||
if ([key isEqualToString:ASTransitionContextFromLayoutKey]) {
|
||||
return _previousLayout.constrainedSizeRange;
|
||||
return _previousLayout->constrainedSize;
|
||||
} else if ([key isEqualToString:ASTransitionContextToLayoutKey]) {
|
||||
return _pendingLayout.constrainedSizeRange;
|
||||
return _pendingLayout->constrainedSize;
|
||||
} else {
|
||||
return ASSizeRangeMake(CGSizeZero, CGSizeZero);
|
||||
}
|
||||
@@ -212,7 +222,7 @@ static inline void findNodesInLayoutAtIndexesWithFilteredNodes(ASLayout *layout,
|
||||
for (ASLayout *sublayout in layout.sublayouts) {
|
||||
if (idx > lastIndex) { break; }
|
||||
if (idx >= firstIndex && [indexes containsIndex:idx]) {
|
||||
ASDisplayNode *node = (ASDisplayNode *)sublayout.layoutableObject;
|
||||
ASDisplayNode *node = (ASDisplayNode *)sublayout.layoutable;
|
||||
ASDisplayNodeCAssert(node, @"A flattened layout must consist exclusively of node sublayouts");
|
||||
// Ignore the odd case in which a non-node sublayout is accessed and the type cast fails
|
||||
if (node != nil) {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style,
|
||||
const ASLayout *layout) {
|
||||
|
||||
__weak id<ASLayoutable> child = layout.layoutableObject;
|
||||
__weak id<ASLayoutable> child = layout.layoutable;
|
||||
switch (style.alignItems) {
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
return child.ascender;
|
||||
@@ -33,7 +33,7 @@ static CGFloat baselineOffset(const ASStackLayoutSpecStyle &style,
|
||||
const CGFloat maxBaseline)
|
||||
{
|
||||
if (style.direction == ASStackLayoutDirectionHorizontal) {
|
||||
__weak id<ASLayoutable> child = l.layoutableObject;
|
||||
__weak id<ASLayoutable> child = l.layoutable;
|
||||
switch (style.alignItems) {
|
||||
case ASStackLayoutAlignItemsBaselineFirst:
|
||||
return maxAscender - child.ascender;
|
||||
@@ -89,9 +89,9 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
|
||||
our layoutSpec to have it so that it can be baseline aligned with another text node or baseline layout spec.
|
||||
*/
|
||||
const auto ascenderIt = std::max_element(positionedLayout.sublayouts.begin(), positionedLayout.sublayouts.end(), [&](const ASLayout *a, const ASLayout *b){
|
||||
return a.layoutableObject.ascender < b.layoutableObject.ascender;
|
||||
return a.layoutable.ascender < b.layoutable.ascender;
|
||||
});
|
||||
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutableObject.ascender;
|
||||
const CGFloat maxAscender = ascenderIt == positionedLayout.sublayouts.end() ? 0 : (*ascenderIt).layoutable.ascender;
|
||||
|
||||
/*
|
||||
Step 3: Take each child and update its layout position based on the baseline offset.
|
||||
@@ -108,7 +108,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
|
||||
CGPoint p = CGPointZero;
|
||||
BOOL first = YES;
|
||||
stackedChildren = AS::map(positionedLayout.sublayouts, [&](ASLayout *l) -> ASLayout *{
|
||||
__weak id<ASLayoutable> child = l.layoutableObject;
|
||||
__weak id<ASLayoutable> child = l.layoutable;
|
||||
p = p + directionPoint(style.direction, child.spacingBefore, 0);
|
||||
if (first) {
|
||||
// if this is the first item use the previously computed start point
|
||||
@@ -161,7 +161,7 @@ ASStackBaselinePositionedLayout ASStackBaselinePositionedLayout::compute(const A
|
||||
const auto descenderIt = std::max_element(stackedChildren.begin(), stackedChildren.end(), [&](const ASLayout *a, const ASLayout *b){
|
||||
return a.position.y + a.size.height < b.position.y + b.size.height;
|
||||
});
|
||||
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutableObject.descender;
|
||||
const CGFloat minDescender = descenderIt == stackedChildren.end() ? 0 : (*descenderIt).layoutable.descender;
|
||||
|
||||
return {stackedChildren, crossSize, maxAscender, minDescender};
|
||||
}
|
||||
|
||||
@@ -23,15 +23,16 @@ static ASLayout *crossChildLayout(const id<ASLayoutable> child,
|
||||
const CGFloat stackMin,
|
||||
const CGFloat stackMax,
|
||||
const CGFloat crossMin,
|
||||
const CGFloat crossMax)
|
||||
const CGFloat crossMax,
|
||||
const CGSize size)
|
||||
{
|
||||
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
|
||||
// stretched children will have a cross dimension of at least crossMin
|
||||
const CGFloat childCrossMin = alignItems == ASStackLayoutAlignItemsStretch ? crossMin : 0;
|
||||
const ASSizeRange childSizeRange = directionSizeRange(style.direction, stackMin, stackMax, childCrossMin, crossMax);
|
||||
ASLayout *layout = [child measureWithSizeRange:childSizeRange];
|
||||
ASLayout *layout = [child layoutThatFits:childSizeRange parentSize:size];
|
||||
ASDisplayNodeCAssertNotNil(layout, @"ASLayout returned from measureWithSizeRange: must not be nil: %@", child);
|
||||
return layout ? : [ASLayout layoutWithLayoutableObject:child constrainedSizeRange:childSizeRange size:CGSizeZero];
|
||||
return layout ? : [ASLayout layoutWithLayoutable:child size:{0, 0}];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,7 +68,8 @@ static ASLayout *crossChildLayout(const id<ASLayoutable> child,
|
||||
@param style the layout style of the overall stack layout
|
||||
*/
|
||||
static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedItem> &layouts,
|
||||
const ASStackLayoutSpecStyle &style)
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const CGSize size)
|
||||
{
|
||||
// Find the maximum cross dimension size among child layouts
|
||||
const auto it = std::max_element(layouts.begin(), layouts.end(),
|
||||
@@ -77,19 +79,16 @@ static void stretchChildrenAlongCrossDimension(std::vector<ASStackUnpositionedIt
|
||||
|
||||
const CGFloat childCrossMax = it == layouts.end() ? 0 : crossDimension(style.direction, it->layout.size);
|
||||
for (auto &l : layouts) {
|
||||
const id<ASLayoutable> child = l.child;
|
||||
const CGSize size = l.layout.size;
|
||||
|
||||
const ASStackLayoutAlignItems alignItems = alignment(child.alignSelf, style.alignItems);
|
||||
const ASStackLayoutAlignItems alignItems = alignment(l.child.alignSelf, style.alignItems);
|
||||
|
||||
const CGFloat cross = crossDimension(style.direction, size);
|
||||
const CGFloat stack = stackDimension(style.direction, size);
|
||||
const CGFloat cross = crossDimension(style.direction, l.layout.size);
|
||||
const CGFloat stack = stackDimension(style.direction, l.layout.size);
|
||||
|
||||
// restretch all stretchable children along the cross axis using the new min. set their max size to childCrossMax,
|
||||
// not crossMax, so that if any of them would choose a larger size just because the min size increased (weird!)
|
||||
// they are forced to choose the same width as all the other children.
|
||||
if (alignItems == ASStackLayoutAlignItemsStretch && std::fabs(cross - childCrossMax) > 0.01) {
|
||||
l.layout = crossChildLayout(child, style, stack, stack, childCrossMax, childCrossMax);
|
||||
l.layout = crossChildLayout(l.child, style, stack, stack, childCrossMax, childCrossMax, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +216,8 @@ ASDISPLAYNODE_INLINE BOOL useOptimizedFlexing(const std::vector<id<ASLayoutable>
|
||||
*/
|
||||
static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem> &items,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange)
|
||||
const ASSizeRange &sizeRange,
|
||||
const CGSize size)
|
||||
{
|
||||
for (ASStackUnpositionedItem &item : items) {
|
||||
const id<ASLayoutable> child = item.child;
|
||||
@@ -227,7 +227,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem
|
||||
0,
|
||||
0,
|
||||
crossDimension(style.direction, sizeRange.min),
|
||||
crossDimension(style.direction, sizeRange.max));
|
||||
crossDimension(style.direction, sizeRange.max),
|
||||
size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,6 +248,7 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector<ASStackUnpositionedItem
|
||||
static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem> &items,
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange,
|
||||
const CGSize size,
|
||||
const BOOL useOptimizedFlexing)
|
||||
{
|
||||
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
|
||||
@@ -258,7 +260,7 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
|
||||
if (flexibleChildren == 0) {
|
||||
// If optimized flexing was used then we have to clean up the unsized children, and lay them out at zero size
|
||||
if (useOptimizedFlexing) {
|
||||
layoutFlexibleChildrenAtZeroSize(items, style, sizeRange);
|
||||
layoutFlexibleChildrenAtZeroSize(items, style, sizeRange, size);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -280,7 +282,8 @@ static void flexChildrenAlongStackDimension(std::vector<ASStackUnpositionedItem>
|
||||
MAX(flexedStackSize, 0),
|
||||
MAX(flexedStackSize, 0),
|
||||
crossDimension(style.direction, sizeRange.min),
|
||||
crossDimension(style.direction, sizeRange.max));
|
||||
crossDimension(style.direction, sizeRange.max),
|
||||
size);
|
||||
isFirstFlex = NO;
|
||||
}
|
||||
}
|
||||
@@ -298,23 +301,19 @@ static std::vector<ASStackUnpositionedItem> layoutChildrenAlongUnconstrainedStac
|
||||
{
|
||||
const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min);
|
||||
const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max);
|
||||
|
||||
return AS::map(children, [&](id<ASLayoutable> child) -> ASStackUnpositionedItem {
|
||||
const ASRelativeDimension flexBasis = child.flexBasis;
|
||||
const BOOL isUnconstrainedFlexBasis = ASRelativeDimensionEqualToRelativeDimension(ASRelativeDimensionUnconstrained, flexBasis);
|
||||
const CGFloat exactStackDimension = ASRelativeDimensionResolve(flexBasis, stackDimension(style.direction, size));
|
||||
|
||||
if (useOptimizedFlexing && isFlexibleInBothDirections(child)) {
|
||||
return { child, [ASLayout layoutWithLayoutableObject:child constrainedSizeRange:sizeRange size:{0, 0}] };
|
||||
return { child, [ASLayout layoutWithLayoutable:child size:{0, 0}] };
|
||||
} else {
|
||||
return {
|
||||
child,
|
||||
crossChildLayout(child,
|
||||
style,
|
||||
isUnconstrainedFlexBasis ? 0 : exactStackDimension,
|
||||
isUnconstrainedFlexBasis ? INFINITY : exactStackDimension,
|
||||
ASDimensionResolve(child.flexBasis, stackDimension(style.direction, size), 0),
|
||||
ASDimensionResolve(child.flexBasis, stackDimension(style.direction, size), INFINITY),
|
||||
minCrossDimension,
|
||||
maxCrossDimension)
|
||||
maxCrossDimension,
|
||||
size)
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -324,9 +323,11 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
|
||||
const ASStackLayoutSpecStyle &style,
|
||||
const ASSizeRange &sizeRange)
|
||||
{
|
||||
// If we have a fixed size in either dimension, pass it to children so they can resolve percentages against it.
|
||||
// Otherwise, we pass ASLayoutableParentDimensionUndefined since it will depend on the content.
|
||||
const CGSize size = {
|
||||
sizeRange.max.width,
|
||||
sizeRange.max.height
|
||||
(sizeRange.min.width == sizeRange.max.width) ? sizeRange.min.width : ASLayoutableParentDimensionUndefined,
|
||||
(sizeRange.min.height == sizeRange.max.height) ? sizeRange.min.height : ASLayoutableParentDimensionUndefined,
|
||||
};
|
||||
|
||||
// We may be able to avoid some redundant layout passes
|
||||
@@ -341,8 +342,8 @@ ASStackUnpositionedLayout ASStackUnpositionedLayout::compute(const std::vector<i
|
||||
size,
|
||||
optimizedFlexing);
|
||||
|
||||
flexChildrenAlongStackDimension(items, style, sizeRange, optimizedFlexing);
|
||||
stretchChildrenAlongCrossDimension(items, style);
|
||||
flexChildrenAlongStackDimension(items, style, sizeRange, size, optimizedFlexing);
|
||||
stretchChildrenAlongCrossDimension(items, style, size);
|
||||
|
||||
const CGFloat stackDimensionSum = computeStackDimensionSum(items, style);
|
||||
return {items, stackDimensionSum, computeViolation(stackDimensionSum, style, sizeRange)};
|
||||
|
||||
@@ -18,7 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
@abstract Creates the stack of TextKit components.
|
||||
@param attributedSeedString The attributed string to seed the returned text storage with, or nil to receive an blank text storage.
|
||||
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and FLT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
|
||||
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
|
||||
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
|
||||
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.
|
||||
*/
|
||||
@@ -28,7 +28,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
/**
|
||||
@abstract Creates the stack of TextKit components.
|
||||
@param textStorage The NSTextStorage to use.
|
||||
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and FLT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
|
||||
@param textContainerSize The size of the text-container. Typically, size specifies the constraining width of the layout, and CGFLOAT_MAX for height. Pass CGSizeZero if these components will be hooked up to a UITextView, which will manage the text container's size itself.
|
||||
@param layoutManager The NSLayoutManager to use.
|
||||
@return An `ASTextKitComponents` containing the created components. The text view component will be nil.
|
||||
@discussion The returned components will be hooked up together, so they are ready for use as a system upon return.
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
// If our text-view's width is already the constrained width, we can use our existing TextKit stack for this sizing calculation.
|
||||
// Otherwise, we create a temporary stack to size for `constrainedWidth`.
|
||||
if (CGRectGetWidth(components.textView.bounds) != constrainedWidth) {
|
||||
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, FLT_MAX)];
|
||||
components = [ASTextKitComponents componentsWithAttributedSeedString:components.textStorage textContainerSize:CGSizeMake(constrainedWidth, CGFLOAT_MAX)];
|
||||
}
|
||||
|
||||
// Force glyph generation and layout, which may not have happened yet (and isn't triggered by -usedRectForTextContainer:).
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
if (_sizingTextContainer == nil) {
|
||||
// make this text container unbounded in height so that the layout manager will compute the total
|
||||
// number of lines and not stop counting when height runs out.
|
||||
_sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, FLT_MAX)];
|
||||
_sizingTextContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(_constrainedSize.width, CGFLOAT_MAX)];
|
||||
_sizingTextContainer.lineFragmentPadding = 0;
|
||||
|
||||
// use 0 regardless of what is in the attributes so that we get an accurate line count
|
||||
@@ -156,7 +156,7 @@
|
||||
|
||||
NSRange longestWordRange = [str rangeOfString:longestWordNeedingResize];
|
||||
NSMutableAttributedString *attrString = [textStorage attributedSubstringFromRange:longestWordRange].mutableCopy;
|
||||
CGSize longestWordSize = [attrString boundingRectWithSize:CGSizeMake(FLT_MAX, FLT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
|
||||
CGSize longestWordSize = [attrString boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil].size;
|
||||
|
||||
// check if the longest word is larger than our constrained width
|
||||
if (longestWordSize.width > _constrainedSize.width) {
|
||||
|
||||
@@ -65,7 +65,7 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi
|
||||
- (CGRect)finalFrameForNode:(ASDisplayNode *)node
|
||||
{
|
||||
for (ASLayout *layout in [self layoutForKey:ASTransitionContextToLayoutKey].sublayouts) {
|
||||
if (layout.layoutableObject == node) {
|
||||
if (layout.layoutable == node) {
|
||||
return [layout frame];
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ NSString * const ASTransitionContextToLayoutKey = @"org.asyncdisplaykit.ASTransi
|
||||
{
|
||||
NSMutableArray<ASDisplayNode *> *subnodes = [NSMutableArray array];
|
||||
for (ASLayout *sublayout in [self layoutForKey:key].sublayouts) {
|
||||
[subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject];
|
||||
[subnodes addObject:(ASDisplayNode *)sublayout.layoutable];
|
||||
}
|
||||
return subnodes;
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@ static const ASSizeRange kSize = {{320, 320}, {320, 320}};
|
||||
- (void)testBackground
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor]);
|
||||
foregroundNode.staticSize = {20, 20};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor], {20, 20});
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
|
||||
@@ -45,10 +45,16 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
|
||||
sizingOptions:(ASCenterLayoutSpecSizingOptions)sizingOptions
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
|
||||
foregroundNode.staticSize = {70, 100};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
|
||||
|
||||
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:[ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:options sizingOptions:sizingOptions child:foregroundNode]background:backgroundNode];
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASCenterLayoutSpec
|
||||
centerLayoutSpecWithCenteringOptions:options
|
||||
sizingOptions:sizingOptions
|
||||
child:foregroundNode]
|
||||
background:backgroundNode];
|
||||
|
||||
[self testLayoutSpec:layoutSpec
|
||||
sizeRange:kSize
|
||||
@@ -83,11 +89,23 @@ static NSString *suffixForCenteringOptions(ASCenterLayoutSpecCenteringOptions ce
|
||||
- (void)testMinimumSizeRangeIsGivenToChildWhenNotCentering
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
foregroundNode.staticSize = {10, 10};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10));
|
||||
foregroundNode.flexGrow = YES;
|
||||
|
||||
ASCenterLayoutSpec *layoutSpec = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringNone sizingOptions:{} child:[ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[foregroundNode]] background:backgroundNode]];
|
||||
ASCenterLayoutSpec *layoutSpec =
|
||||
[ASCenterLayoutSpec
|
||||
centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringNone
|
||||
sizingOptions:{}
|
||||
child:
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStart
|
||||
children:@[foregroundNode]]
|
||||
background:backgroundNode]];
|
||||
|
||||
[self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil];
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[stack1, stack2, node5]];
|
||||
};
|
||||
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
|
||||
XCTAssertEqual(node.subnodes[0], node5);
|
||||
XCTAssertEqual(node.subnodes[1], node1);
|
||||
XCTAssertEqual(node.subnodes[2], node2);
|
||||
@@ -104,13 +104,13 @@
|
||||
}
|
||||
};
|
||||
|
||||
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
|
||||
XCTAssertEqual(node.subnodes[0], node1);
|
||||
XCTAssertEqual(node.subnodes[1], node2);
|
||||
|
||||
node.layoutState = @2;
|
||||
[node invalidateCalculatedLayout];
|
||||
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
|
||||
|
||||
XCTAssertEqual(node.subnodes[0], node1);
|
||||
XCTAssertEqual(node.subnodes[1], node3);
|
||||
@@ -157,12 +157,12 @@
|
||||
|
||||
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
|
||||
XCTAssertEqual(node.subnodes[0], node1);
|
||||
|
||||
node.layoutState = @2;
|
||||
[node invalidateCalculatedLayout];
|
||||
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
|
||||
|
||||
// Dispatch back to the main thread to let the insertion / deletion of subnodes happening
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@@ -202,7 +202,7 @@
|
||||
|
||||
XCTestExpectation *expectation = [self expectationWithDescription:@"Fix IHM layout transition also if one node is already loaded"];
|
||||
|
||||
[node measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeZero)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero)];
|
||||
XCTAssertEqual(node.subnodes[0], node1);
|
||||
|
||||
node.layoutState = @2;
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
{
|
||||
CGSize nodeSize = CGSizeMake(100, 100);
|
||||
|
||||
ASStaticSizeDisplayNode *displayNode = [ASStaticSizeDisplayNode new];
|
||||
displayNode.staticSize = nodeSize;
|
||||
ASDisplayNode *displayNode = [[ASDisplayNode alloc] init];
|
||||
displayNode.width = ASDimensionMake(100);
|
||||
displayNode.height = ASDimensionMake(100);
|
||||
|
||||
// Use a button node in here as ASButtonNode uses layoutSpecThatFits:
|
||||
ASButtonNode *buttonNode = [ASButtonNode new];
|
||||
@@ -47,8 +48,8 @@
|
||||
{
|
||||
CGSize nodeSize = CGSizeMake(100, 100);
|
||||
|
||||
ASStaticSizeDisplayNode *displayNode = [ASStaticSizeDisplayNode new];
|
||||
displayNode.staticSize = nodeSize;
|
||||
ASDisplayNode *displayNode = [ASDisplayNode new];
|
||||
[displayNode setSizeWithCGSize:nodeSize];
|
||||
|
||||
ASButtonNode *buttonNode = [ASButtonNode new];
|
||||
[displayNode addSubnode:buttonNode];
|
||||
@@ -79,7 +80,7 @@
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:someOtherNode];
|
||||
};
|
||||
|
||||
XCTAssertThrows([displayNode measure:CGSizeMake(100, 100)], @"Should throw if subnode was added in layoutSpecThatFits:");
|
||||
XCTAssertThrows([displayNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))], @"Should throw if subnode was added in layoutSpecThatFits:");
|
||||
}
|
||||
|
||||
- (void)testNotAllowModifyingSubnodesInLayoutSpecThatFits
|
||||
@@ -95,7 +96,7 @@
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:someOtherNode];
|
||||
};
|
||||
|
||||
XCTAssertThrows([displayNode measure:CGSizeMake(100, 100)], @"Should throw if subnodes where modified in layoutSpecThatFits:");
|
||||
XCTAssertThrows([displayNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))], @"Should throw if subnodes where modified in layoutSpecThatFits:");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -103,8 +104,8 @@
|
||||
{
|
||||
CGSize nodeSize = CGSizeMake(100, 100);
|
||||
|
||||
ASStaticSizeDisplayNode *displayNode = [ASStaticSizeDisplayNode new];
|
||||
displayNode.staticSize = nodeSize;
|
||||
ASDisplayNode *displayNode = [ASDisplayNode new];
|
||||
[displayNode setSizeWithCGSize:nodeSize];
|
||||
|
||||
ASButtonNode *buttonNode = [ASButtonNode new];
|
||||
[displayNode addSubnode:buttonNode];
|
||||
@@ -121,7 +122,7 @@
|
||||
[displayNode.view layoutIfNeeded];
|
||||
XCTAssertEqual(numberOfLayoutSpecThatFitsCalls, 1, @"Should measure during layout if not measured");
|
||||
|
||||
[displayNode measureWithSizeRange:ASSizeRangeMake(nodeSize, nodeSize)];
|
||||
[displayNode layoutThatFits:ASSizeRangeMake(nodeSize, nodeSize)];
|
||||
XCTAssertEqual(numberOfLayoutSpecThatFitsCalls, 1, @"Should not remeasure with same bounds");
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
node.layoutSpecBlock = ^(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(5, 5, 5, 5) child:subnode];
|
||||
};
|
||||
[node measure:CGSizeMake(100, 100)];
|
||||
[node layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
|
||||
|
||||
ASSnapshotVerifyNode(node, nil);
|
||||
}
|
||||
|
||||
@@ -1991,7 +1991,7 @@ static bool stringContainsPointer(NSString *description, id p) {
|
||||
- (void)DISABLED_testThatItsSafeToAutomeasureANodeMidTransition
|
||||
{
|
||||
ASDisplayNode *supernode = [[ASDisplayNode alloc] init];
|
||||
[supernode measure:CGSizeMake(100, 100)];
|
||||
[supernode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
|
||||
ASDisplayNode *node = [[ASDisplayNode alloc] init];
|
||||
node.bounds = CGRectMake(0, 0, 50, 50);
|
||||
[supernode addSubnode:node];
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASEditableTextNode.h>
|
||||
|
||||
static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
@@ -138,23 +139,11 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
XCTAssertTrue(secureEditableTextNode.textView.secureTextEntry == YES, @"textView's isSecureTextEntry should be YES.");
|
||||
}
|
||||
|
||||
- (void)testSetPreferredFrameSize
|
||||
{
|
||||
CGSize preferredFrameSize = CGSizeMake(100, 100);
|
||||
_editableTextNode.preferredFrameSize = preferredFrameSize;
|
||||
|
||||
CGSize calculatedSize = [_editableTextNode measure:CGSizeZero];
|
||||
XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated width (%f) should be equal to preferred width (%f)", calculatedSize.width, preferredFrameSize.width);
|
||||
XCTAssertTrue(calculatedSize.width != preferredFrameSize.width, @"Calculated height (%f) should be equal to preferred height (%f)", calculatedSize.width, preferredFrameSize.width);
|
||||
|
||||
_editableTextNode.preferredFrameSize = CGSizeZero;
|
||||
}
|
||||
|
||||
- (void)testCalculatedSizeIsGreaterThanOrEqualToConstrainedSize
|
||||
{
|
||||
for (NSInteger i = 10; i < 500; i += 50) {
|
||||
CGSize constrainedSize = CGSizeMake(i, i);
|
||||
CGSize calculatedSize = [_editableTextNode measure:constrainedSize];
|
||||
CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
XCTAssertTrue(calculatedSize.width <= constrainedSize.width, @"Calculated width (%f) should be less than or equal to constrained width (%f)", calculatedSize.width, constrainedSize.width);
|
||||
XCTAssertTrue(calculatedSize.height <= constrainedSize.height, @"Calculated height (%f) should be less than or equal to constrained height (%f)", calculatedSize.height, constrainedSize.height);
|
||||
}
|
||||
@@ -164,8 +153,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
{
|
||||
for (NSInteger i = 10; i < 500; i += 50) {
|
||||
CGSize constrainedSize = CGSizeMake(i, i);
|
||||
CGSize calculatedSize = [_editableTextNode measure:constrainedSize];
|
||||
CGSize recalculatedSize = [_editableTextNode measure:calculatedSize];
|
||||
CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
CGSize recalculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
|
||||
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 4.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
|
||||
}
|
||||
@@ -175,8 +164,8 @@ static BOOL CGSizeEqualToSizeWithIn(CGSize size1, CGSize size2, CGFloat delta)
|
||||
{
|
||||
for (CGFloat i = 10; i < 500; i *= 1.3) {
|
||||
CGSize constrainedSize = CGSizeMake(i, i);
|
||||
CGSize calculatedSize = [_editableTextNode measure:constrainedSize];
|
||||
CGSize recalculatedSize = [_editableTextNode measure:calculatedSize];
|
||||
CGSize calculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
CGSize recalculatedSize = [_editableTextNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
|
||||
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 11.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
- (UIImage *)testImage
|
||||
{
|
||||
NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"logo-square"
|
||||
ofType:@"png" inDirectory:@"TestResources"];
|
||||
ofType:@"png"
|
||||
inDirectory:@"TestResources"];
|
||||
return [UIImage imageWithContentsOfFile:path];
|
||||
}
|
||||
|
||||
@@ -29,30 +30,32 @@
|
||||
// trivial test case to ensure ASSnapshotTestCase works
|
||||
ASImageNode *imageNode = [[ASImageNode alloc] init];
|
||||
imageNode.image = [self testImage];
|
||||
[imageNode measure:CGSizeMake(100, 100)];
|
||||
[imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
|
||||
|
||||
ASSnapshotVerifyNode(imageNode, nil);
|
||||
}
|
||||
|
||||
- (void)testForcedScaling
|
||||
{
|
||||
ASImageNode *imageNode = [[ASImageNode alloc] init];
|
||||
CGSize forcedImageSize = CGSizeMake(100, 100);
|
||||
|
||||
ASImageNode *imageNode = [[ASImageNode alloc] init];
|
||||
imageNode.forcedSize = forcedImageSize;
|
||||
imageNode.image = [self testImage];
|
||||
imageNode.forcedSize = CGSizeMake(100, 100);
|
||||
|
||||
// Snapshot testing requires that node is formally laid out.
|
||||
imageNode.preferredFrameSize = CGSizeMake(100, 100);
|
||||
[imageNode measure:CGSizeMake(100, 100)];
|
||||
|
||||
[imageNode setSizeWithCGSize:forcedImageSize];
|
||||
[imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, forcedImageSize)];
|
||||
ASSnapshotVerifyNode(imageNode, @"first");
|
||||
|
||||
imageNode.preferredFrameSize = CGSizeMake(200, 200);
|
||||
[imageNode measure:CGSizeMake(200, 200)];
|
||||
[imageNode setSizeWithCGSize:CGSizeMake(200, 200)];
|
||||
[imageNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(200, 200))];
|
||||
|
||||
ASSnapshotVerifyNode(imageNode, @"second");
|
||||
|
||||
XCTAssert(CGImageGetWidth((CGImageRef)imageNode.contents) == 100 * imageNode.contentsScale && CGImageGetHeight((CGImageRef)imageNode.contents) == 100 * imageNode.contentsScale, @"contents should be 100 x 100 by contents scale.");
|
||||
XCTAssert(CGImageGetWidth((CGImageRef)imageNode.contents) == forcedImageSize.width * imageNode.contentsScale &&
|
||||
CGImageGetHeight((CGImageRef)imageNode.contents) == forcedImageSize.height * imageNode.contentsScale,
|
||||
@"Contents should be 100 x 100 by contents scale.");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -50,12 +50,14 @@ static NSString *nameForInsets(UIEdgeInsets insets)
|
||||
for (NSUInteger combination = 0; combination < 16; combination++) {
|
||||
UIEdgeInsets insets = insetsForCombination(combination, 10);
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
|
||||
foregroundNode.staticSize = {10, 10};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {10, 10});
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode]
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:insets
|
||||
child:foregroundNode]
|
||||
background:backgroundNode];
|
||||
|
||||
static ASSizeRange kVariableSize = {{0, 0}, {300, 300}};
|
||||
@@ -71,12 +73,14 @@ static NSString *nameForInsets(UIEdgeInsets insets)
|
||||
for (NSUInteger combination = 0; combination < 16; combination++) {
|
||||
UIEdgeInsets insets = insetsForCombination(combination, 10);
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
|
||||
foregroundNode.staticSize = {10, 10};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {10, 10});
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode]
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:insets
|
||||
child:foregroundNode]
|
||||
background:backgroundNode];
|
||||
|
||||
static ASSizeRange kFixedSize = {{300, 300}, {300, 300}};
|
||||
@@ -93,12 +97,14 @@ static NSString *nameForInsets(UIEdgeInsets insets)
|
||||
for (NSUInteger combination = 0; combination < 16; combination++) {
|
||||
UIEdgeInsets insets = insetsForCombination(combination, 0);
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor grayColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
|
||||
foregroundNode.staticSize = {10, 10};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], {10, 10});
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:[ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:foregroundNode]
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:insets
|
||||
child:foregroundNode]
|
||||
background:backgroundNode];
|
||||
|
||||
static ASSizeRange kFixedSize = {{300, 300}, {300, 300}};
|
||||
|
||||
@@ -31,17 +31,16 @@
|
||||
identifier:(NSString *)identifier;
|
||||
@end
|
||||
|
||||
@interface ASStaticSizeDisplayNode : ASDisplayNode
|
||||
|
||||
@property (nonatomic) CGSize staticSize;
|
||||
|
||||
@end
|
||||
|
||||
static inline ASStaticSizeDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor)
|
||||
{
|
||||
ASStaticSizeDisplayNode *node = [[ASStaticSizeDisplayNode alloc] init];
|
||||
__attribute__((overloadable)) static inline ASDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor, CGSize size) {
|
||||
ASDisplayNode *node = [[ASDisplayNode alloc] init];
|
||||
node.layerBacked = YES;
|
||||
node.backgroundColor = backgroundColor;
|
||||
node.staticSize = CGSizeZero;
|
||||
node.width = ASDimensionMakeWithPoints(size.width);
|
||||
node.height = ASDimensionMakeWithPoints(size.height);
|
||||
return node;
|
||||
}
|
||||
|
||||
__attribute__((overloadable)) static inline ASDisplayNode *ASDisplayNodeWithBackgroundColor(UIColor *backgroundColor)
|
||||
{
|
||||
return ASDisplayNodeWithBackgroundColor(backgroundColor, CGSizeZero);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
node.layoutSpecUnderTest = layoutSpec;
|
||||
|
||||
[node measureWithSizeRange:sizeRange];
|
||||
[node layoutThatFits:sizeRange];
|
||||
ASSnapshotVerifyNode(node, identifier);
|
||||
}
|
||||
|
||||
@@ -60,12 +60,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASStaticSizeDisplayNode
|
||||
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
{
|
||||
return _staticSize;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -23,8 +23,7 @@ static const ASSizeRange kSize = {{320, 320}, {320, 320}};
|
||||
- (void)testOverlay
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor]);
|
||||
foregroundNode.staticSize = {20, 20};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blackColor], {20, 20});
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASOverlayLayoutSpec
|
||||
|
||||
@@ -21,8 +21,7 @@ static const ASSizeRange kFixedSize = {{0, 0}, {100, 100}};
|
||||
|
||||
- (void)testRatioLayoutSpecWithRatio:(CGFloat)ratio childSize:(CGSize)childSize identifier:(NSString *)identifier
|
||||
{
|
||||
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
|
||||
subnode.staticSize = childSize;
|
||||
ASDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], childSize);
|
||||
|
||||
ASLayoutSpec *layoutSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:subnode];
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
|
||||
|
||||
- (void)testWithOptions
|
||||
{
|
||||
|
||||
[self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionStart];
|
||||
[self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionCenter];
|
||||
[self testAllVerticalPositionsForHorizontalPosition:ASRelativeLayoutSpecPositionEnd];
|
||||
@@ -57,14 +56,16 @@ static const ASSizeRange kSize = {{100, 120}, {320, 160}};
|
||||
sizingOptions:(ASRelativeLayoutSpecSizingOption)sizingOptions
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor]);
|
||||
foregroundNode.staticSize = {70, 100};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASRelativeLayoutSpec
|
||||
relativePositionLayoutSpecWithHorizontalPosition:horizontalPosition verticalPosition:verticalPosition sizingOption:sizingOptions child:foregroundNode]
|
||||
relativePositionLayoutSpecWithHorizontalPosition:horizontalPosition
|
||||
verticalPosition:verticalPosition
|
||||
sizingOption:sizingOptions
|
||||
child:foregroundNode]
|
||||
background:backgroundNode];
|
||||
|
||||
[self testLayoutSpec:layoutSpec
|
||||
@@ -105,17 +106,26 @@ static NSString *suffixForPositionOptions(ASRelativeLayoutSpecPosition horizonta
|
||||
- (void)testMinimumSizeRangeIsGivenToChildWhenNotPositioning
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
foregroundNode.staticSize = {10, 10};
|
||||
ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor], CGSizeMake(10, 10));
|
||||
foregroundNode.flexGrow = YES;
|
||||
|
||||
ASLayoutSpec *childSpec = [ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[foregroundNode]]
|
||||
background:backgroundNode];
|
||||
ASLayoutSpec *childSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStart
|
||||
children:@[foregroundNode]]
|
||||
background:backgroundNode];
|
||||
|
||||
ASRelativeLayoutSpec *layoutSpec = [ASRelativeLayoutSpec
|
||||
relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart verticalPosition:ASRelativeLayoutSpecPositionStart sizingOption:{} child:childSpec];
|
||||
|
||||
ASRelativeLayoutSpec *layoutSpec =
|
||||
[ASRelativeLayoutSpec
|
||||
relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart
|
||||
verticalPosition:ASRelativeLayoutSpecPositionStart
|
||||
sizingOption:{}
|
||||
child:childSpec];
|
||||
|
||||
[self testLayoutSpec:layoutSpec sizeRange:kSize subnodes:@[backgroundNode, foregroundNode] identifier:nil];
|
||||
}
|
||||
|
||||
@@ -23,26 +23,31 @@
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
static NSArray *defaultSubnodes()
|
||||
static NSArray<ASDisplayNode *> *defaultSubnodes()
|
||||
{
|
||||
return defaultSubnodesWithSameSize(CGSizeZero, NO);
|
||||
}
|
||||
|
||||
static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
static NSArray<ASDisplayNode *> *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
NSArray *subnodes = @[
|
||||
ASDisplayNodeWithBackgroundColor([UIColor redColor]),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor blueColor]),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor greenColor])
|
||||
];
|
||||
for (ASStaticSizeDisplayNode *subnode in subnodes) {
|
||||
subnode.staticSize = subnodeSize;
|
||||
NSArray<ASDisplayNode *> *subnodes = @[
|
||||
ASDisplayNodeWithBackgroundColor([UIColor redColor], subnodeSize),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor blueColor], subnodeSize),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor greenColor], subnodeSize)
|
||||
];
|
||||
for (ASDisplayNode *subnode in subnodes) {
|
||||
subnode.flexGrow = flex;
|
||||
subnode.flexShrink = flex;
|
||||
}
|
||||
return subnodes;
|
||||
}
|
||||
|
||||
static void setCGSizeToNode(CGSize size, ASDisplayNode *node)
|
||||
{
|
||||
node.width = ASDimensionMakeWithPoints(size.width);
|
||||
node.height = ASDimensionMakeWithPoints(size.height);
|
||||
}
|
||||
|
||||
- (void)testStackLayoutSpecWithJustify:(ASStackLayoutJustifyContent)justify
|
||||
flex:(BOOL)flex
|
||||
sizeRange:(ASSizeRange)sizeRange
|
||||
@@ -53,7 +58,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.justifyContent = justify
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, flex);
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, flex);
|
||||
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:sizeRange subnodes:subnodes identifier:identifier];
|
||||
}
|
||||
@@ -63,13 +68,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
itemsVerticalAlignment:(ASVerticalAlignment)verticalAlignment
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
|
||||
ASStackLayoutSpec *stackLayoutSpec = [[ASStackLayoutSpec alloc] init];
|
||||
stackLayoutSpec.direction = direction;
|
||||
stackLayoutSpec.children = subnodes;
|
||||
[stackLayoutSpec setHorizontalAlignment:horizontalAlignment];
|
||||
[stackLayoutSpec setVerticalAlignment:verticalAlignment];
|
||||
stackLayoutSpec.horizontalAlignment = horizontalAlignment;
|
||||
stackLayoutSpec.verticalAlignment = verticalAlignment;
|
||||
|
||||
CGSize exactSize = CGSizeMake(200, 200);
|
||||
static ASSizeRange kSize = ASSizeRangeMake(exactSize, exactSize);
|
||||
@@ -90,11 +95,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
subnodes:(NSArray *)subnodes
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
ASStackLayoutSpec *stackLayoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:style.direction
|
||||
spacing:style.spacing
|
||||
justifyContent:style.justifyContent
|
||||
alignItems:style.alignItems
|
||||
children:children];
|
||||
ASStackLayoutSpec *stackLayoutSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:style.direction
|
||||
spacing:style.spacing
|
||||
justifyContent:style.justifyContent
|
||||
alignItems:style.alignItems
|
||||
children:children];
|
||||
|
||||
[self testStackLayoutSpec:stackLayoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
|
||||
}
|
||||
|
||||
@@ -104,7 +112,6 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
identifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor whiteColor]);
|
||||
|
||||
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:stackLayoutSpec background:backgroundNode];
|
||||
|
||||
NSMutableArray *newSubnodes = [NSMutableArray arrayWithObject:backgroundNode];
|
||||
@@ -145,8 +152,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
((ASDisplayNode *)subnodes[1]).flexShrink = YES;
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
subnodes[1].flexShrink = YES;
|
||||
|
||||
// Width is 75px--that's less than the sum of the widths of the children, which is 100px.
|
||||
static ASSizeRange kSize = {{75, 0}, {75, 150}};
|
||||
@@ -157,8 +164,8 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, YES);
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, YES);
|
||||
setCGSizeToNode({150, 150}, subnodes[1]);
|
||||
|
||||
// width 300px; height 0-150px.
|
||||
static ASSizeRange kUnderflowSize = {{300, 0}, {300, 150}};
|
||||
@@ -173,10 +180,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 50};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 50}, subnodes[1]);
|
||||
setCGSizeToNode({150, 50}, subnodes[2]);
|
||||
|
||||
// width 0-300px; height 300px
|
||||
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
|
||||
@@ -194,10 +201,10 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.spacing = 10
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 50};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 50}, subnodes[1]);
|
||||
setCGSizeToNode({150, 50}, subnodes[2]);
|
||||
|
||||
// width 0-300px; height 300px
|
||||
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
|
||||
@@ -212,12 +219,16 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:{10, 10, 10 ,10}
|
||||
insetLayoutSpecWithInsets:{10, 10, 10, 10}
|
||||
child:
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:10 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[]]
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:10
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[]]
|
||||
background:backgroundNode]];
|
||||
|
||||
// width 300px; height 0-300px
|
||||
@@ -231,28 +242,28 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
static ASSizeRange kAnySize = {{0, 0}, {INFINITY, INFINITY}};
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionVertical};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 10;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 20;
|
||||
subnodes[1].spacingBefore = 10;
|
||||
subnodes[2].spacingBefore = 20;
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBefore"];
|
||||
// Reset above spacing values
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 0;
|
||||
subnodes[2].spacingBefore = 0;
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 10;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 20;
|
||||
subnodes[1].spacingAfter = 10;
|
||||
subnodes[2].spacingAfter = 20;
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingAfter"];
|
||||
// Reset above spacing values
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingAfter = 0;
|
||||
subnodes[1].spacingAfter = 0;
|
||||
subnodes[2].spacingAfter = 0;
|
||||
|
||||
style.spacing = 10;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = -10;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingAfter = -10;
|
||||
subnodes[1].spacingBefore = -10;
|
||||
subnodes[1].spacingAfter = -10;
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kAnySize subnodes:subnodes identifier:@"spacingBalancedOut"];
|
||||
}
|
||||
|
||||
@@ -263,14 +274,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.justifyContent = ASStackLayoutJustifyContentCenter
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
|
||||
subnodes[0].spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 20;
|
||||
subnodes[2].spacingBefore = 30;
|
||||
|
||||
// width 0-300px; height 300px
|
||||
static ASSizeRange kVariableHeight = {{0, 300}, {300, 300}};
|
||||
@@ -284,8 +295,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.justifyContent = ASStackLayoutJustifyContentSpaceBetween
|
||||
};
|
||||
|
||||
ASStaticSizeDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
child.staticSize = {50, 50};
|
||||
ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
|
||||
|
||||
// width 300px; height 0-INF
|
||||
static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}};
|
||||
@@ -299,8 +309,7 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.justifyContent = ASStackLayoutJustifyContentSpaceAround
|
||||
};
|
||||
|
||||
ASStaticSizeDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
child.staticSize = {50, 50};
|
||||
ASDisplayNode *child = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
|
||||
|
||||
// width 300px; height 0-INF
|
||||
static ASSizeRange kVariableHeight = {{300, 0}, {300, INFINITY}};
|
||||
@@ -325,12 +334,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
ASStaticSizeDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
ASStaticSizeDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
subnode2.staticSize = {50, 50};
|
||||
ASDisplayNode * subnode1 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
ASDisplayNode * subnode2 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {50, 50});
|
||||
|
||||
ASRatioLayoutSpec *child1 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.5 child:subnode1];
|
||||
child1.flexBasis = ASRelativeDimensionMakeWithFraction(1);
|
||||
child1.flexBasis = ASDimensionMakeWithFraction(1);
|
||||
child1.flexGrow = YES;
|
||||
child1.flexShrink = YES;
|
||||
|
||||
@@ -345,15 +353,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.alignItems = ASStackLayoutAlignItemsCenter
|
||||
};
|
||||
|
||||
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
subnode1.staticSize = {100, 100};
|
||||
ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100});
|
||||
subnode1.flexShrink = YES;
|
||||
|
||||
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
subnode2.staticSize = {50, 50};
|
||||
ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50});
|
||||
subnode2.flexShrink = YES;
|
||||
|
||||
NSArray *subnodes = @[subnode1, subnode2];
|
||||
NSArray<ASDisplayNode *> *subnodes = @[subnode1, subnode2];
|
||||
static ASSizeRange kFixedWidth = {{150, 0}, {150, 100}};
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil];
|
||||
}
|
||||
@@ -362,14 +368,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
ASStaticSizeDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
subnode1.staticSize = {100, 100};
|
||||
ASDisplayNode *subnode1 = ASDisplayNodeWithBackgroundColor([UIColor redColor], {100, 100});
|
||||
|
||||
ASStaticSizeDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
subnode2.staticSize = {50, 50};
|
||||
ASDisplayNode *subnode2 = ASDisplayNodeWithBackgroundColor([UIColor blueColor], {50, 50});
|
||||
subnode2.alignSelf = ASStackLayoutAlignSelfCenter;
|
||||
|
||||
NSArray *subnodes = @[subnode1, subnode2];
|
||||
NSArray<ASDisplayNode *> *subnodes = @[subnode1, subnode2];
|
||||
static ASSizeRange kFixedWidth = {{150, 0}, {150, INFINITY}};
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kFixedWidth subnodes:subnodes identifier:nil];
|
||||
}
|
||||
@@ -382,14 +386,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.alignItems = ASStackLayoutAlignItemsStart
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
|
||||
subnodes[0].spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 20;
|
||||
subnodes[2].spacingBefore = 30;
|
||||
|
||||
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
|
||||
@@ -403,14 +407,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.alignItems = ASStackLayoutAlignItemsEnd
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
|
||||
subnodes[0].spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 20;
|
||||
subnodes[2].spacingBefore = 30;
|
||||
|
||||
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
|
||||
@@ -424,14 +428,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.alignItems = ASStackLayoutAlignItemsCenter
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
|
||||
subnodes[0].spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 20;
|
||||
subnodes[2].spacingBefore = 30;
|
||||
|
||||
static ASSizeRange kExactSize = {{300, 300}, {300, 300}};
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kExactSize subnodes:subnodes identifier:nil];
|
||||
@@ -445,14 +449,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.alignItems = ASStackLayoutAlignItemsStretch
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
|
||||
subnodes[0].spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 20;
|
||||
subnodes[2].spacingBefore = 30;
|
||||
|
||||
static ASSizeRange kVariableSize = {{200, 200}, {300, 300}};
|
||||
// all children should be 200px wide
|
||||
@@ -467,14 +471,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
.alignItems = ASStackLayoutAlignItemsStretch
|
||||
};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 70};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {150, 90};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 70}, subnodes[1]);
|
||||
setCGSizeToNode({150, 90}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).spacingBefore = 0;
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).spacingBefore = 20;
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).spacingBefore = 30;
|
||||
subnodes[0].spacingBefore = 0;
|
||||
subnodes[1].spacingBefore = 20;
|
||||
subnodes[2].spacingBefore = 30;
|
||||
|
||||
static ASSizeRange kVariableSize = {{50, 50}, {300, 300}};
|
||||
// all children should be 150px wide
|
||||
@@ -491,12 +495,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
setCGSizeToNode({150, 150}, subnodes[1]);
|
||||
|
||||
for (ASStaticSizeDisplayNode *subnode in subnodes) {
|
||||
for (ASDisplayNode *subnode in subnodes) {
|
||||
subnode.flexGrow = YES;
|
||||
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(10);
|
||||
subnode.flexBasis = ASDimensionMakeWithPoints(10);
|
||||
}
|
||||
|
||||
// width 300px; height 0-150px.
|
||||
@@ -512,15 +516,14 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
NSArray *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
|
||||
for (ASStaticSizeDisplayNode *subnode in subnodes) {
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodesWithSameSize({50, 50}, NO);
|
||||
for (ASDisplayNode *subnode in subnodes) {
|
||||
subnode.flexGrow = YES;
|
||||
}
|
||||
|
||||
// This should override the intrinsic size of 50pts and instead compute to 50% = 100pts.
|
||||
// The result should be that the red box is twice as wide as the blue and gree boxes after flexing.
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).flexBasis = ASRelativeDimensionMakeWithFraction(0.5);
|
||||
subnodes[0].flexBasis = ASDimensionMakeWithFraction(0.5);
|
||||
|
||||
static ASSizeRange kSize = {{200, 0}, {200, INFINITY}};
|
||||
[self testStackLayoutSpecWithStyle:style sizeRange:kSize subnodes:subnodes identifier:nil];
|
||||
@@ -530,13 +533,13 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {50, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {150, 150};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {50, 50};
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({50, 50}, subnodes[0]);
|
||||
setCGSizeToNode({150, 150}, subnodes[1]);
|
||||
setCGSizeToNode({150, 50}, subnodes[2]);
|
||||
|
||||
for (ASStaticSizeDisplayNode *subnode in subnodes) {
|
||||
subnode.flexBasis = ASRelativeDimensionMakeWithPoints(20);
|
||||
for (ASDisplayNode *subnode in subnodes) {
|
||||
subnode.flexBasis = ASDimensionMakeWithPoints(20);
|
||||
}
|
||||
|
||||
static ASSizeRange kSize = {{300, 0}, {300, 150}};
|
||||
@@ -545,13 +548,11 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
|
||||
- (void)testCrossAxisStretchingOccursAfterStackAxisFlexing
|
||||
{
|
||||
NSArray *subnodes = @[
|
||||
ASDisplayNodeWithBackgroundColor([UIColor greenColor]),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor blueColor]),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor redColor])
|
||||
];
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {10, 0};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {3000, 3000};
|
||||
NSArray<ASDisplayNode *> *subnodes = @[
|
||||
ASDisplayNodeWithBackgroundColor([UIColor greenColor]),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor blueColor], {10, 0}),
|
||||
ASDisplayNodeWithBackgroundColor([UIColor redColor], {3000, 3000})
|
||||
];
|
||||
|
||||
ASRatioLayoutSpec *child2 = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0 child:subnodes[2]];
|
||||
child2.flexGrow = YES;
|
||||
@@ -565,7 +566,12 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10)
|
||||
child:
|
||||
[ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStretch children:@[subnodes[1], child2,]]
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[subnodes[1], child2]]
|
||||
]
|
||||
background:subnodes[0]];
|
||||
|
||||
@@ -576,17 +582,16 @@ static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex)
|
||||
- (void)testViolationIsDistributedEquallyAmongFlexibleChildren
|
||||
{
|
||||
ASStackLayoutSpecStyle style = {.direction = ASStackLayoutDirectionHorizontal};
|
||||
|
||||
NSArray<ASDisplayNode *> *subnodes = defaultSubnodes();
|
||||
|
||||
NSArray *subnodes = defaultSubnodes();
|
||||
setCGSizeToNode({300, 50}, subnodes[0]);
|
||||
setCGSizeToNode({100, 50}, subnodes[1]);
|
||||
setCGSizeToNode({200, 50}, subnodes[2]);
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).staticSize = {300, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[0]).flexShrink = YES;
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).staticSize = {100, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[1]).flexShrink = NO;
|
||||
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).staticSize = {200, 50};
|
||||
((ASStaticSizeDisplayNode *)subnodes[2]).flexShrink = YES;
|
||||
subnodes[0].flexShrink = YES;
|
||||
subnodes[1].flexShrink = NO;
|
||||
subnodes[2].flexShrink = YES;
|
||||
|
||||
// A width of 400px results in a violation of 200px. This is distributed equally among each flexible child,
|
||||
// causing both of them to be shrunk by 100px, resulting in widths of 300px, 100px, and 50px.
|
||||
|
||||
@@ -22,46 +22,34 @@
|
||||
|
||||
- (void)testSizingBehaviour
|
||||
{
|
||||
[self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(FLT_MAX, FLT_MAX))
|
||||
[self testWithSizeRange:ASSizeRangeMake(CGSizeMake(150, 200), CGSizeMake(INFINITY, INFINITY))
|
||||
identifier:@"underflowChildren"];
|
||||
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(50, 100))
|
||||
identifier:@"overflowChildren"];
|
||||
// Expect the spec to wrap its content because children sizes are between constrained size
|
||||
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(FLT_MAX / 2, FLT_MAX / 2))
|
||||
[self testWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(INFINITY / 2, INFINITY / 2))
|
||||
identifier:@"wrappedChildren"];
|
||||
}
|
||||
|
||||
- (void)testChildrenMeasuredWithAutoMaxSize
|
||||
{
|
||||
ASStaticSizeDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor], (CGSize){50, 50});
|
||||
firstChild.layoutPosition = CGPointMake(0, 0);
|
||||
firstChild.staticSize = CGSizeMake(50, 50);
|
||||
|
||||
ASStaticSizeDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor], (CGSize){100, 100});
|
||||
secondChild.layoutPosition = CGPointMake(10, 60);
|
||||
secondChild.staticSize = CGSizeMake(100, 100);
|
||||
|
||||
ASSizeRange sizeRange = ASSizeRangeMake(CGSizeMake(10, 10), CGSizeMake(110, 160));
|
||||
[self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:nil];
|
||||
|
||||
XCTAssertTrue(ASSizeRangeEqualToSizeRange(firstChild.constrainedSizeForCalculatedLayout,
|
||||
ASSizeRangeMake(CGSizeZero, sizeRange.max)));
|
||||
CGSize secondChildMaxSize = CGSizeMake(sizeRange.max.width - secondChild.layoutPosition.x,
|
||||
sizeRange.max.height - secondChild.layoutPosition.y);
|
||||
XCTAssertTrue(ASSizeRangeEqualToSizeRange(secondChild.constrainedSizeForCalculatedLayout,
|
||||
ASSizeRangeMake(CGSizeZero, secondChildMaxSize)));
|
||||
}
|
||||
|
||||
- (void)testWithSizeRange:(ASSizeRange)sizeRange identifier:(NSString *)identifier
|
||||
{
|
||||
ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
|
||||
ASDisplayNode *firstChild = ASDisplayNodeWithBackgroundColor([UIColor redColor], (CGSize){50, 50});
|
||||
firstChild.layoutPosition = CGPointMake(0, 0);
|
||||
firstChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(50, 50));
|
||||
|
||||
|
||||
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
|
||||
ASDisplayNode *secondChild = ASDisplayNodeWithBackgroundColor([UIColor blueColor], (CGSize){100, 100});
|
||||
secondChild.layoutPosition = CGPointMake(0, 50);
|
||||
secondChild.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(100, 100));
|
||||
|
||||
[self testWithChildren:@[firstChild, secondChild] sizeRange:sizeRange identifier:identifier];
|
||||
}
|
||||
@@ -73,9 +61,12 @@
|
||||
NSMutableArray *subnodes = [NSMutableArray arrayWithArray:children];
|
||||
[subnodes insertObject:backgroundNode atIndex:0];
|
||||
|
||||
ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:children];
|
||||
ASLayoutSpec *layoutSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:staticLayoutSpec
|
||||
background:backgroundNode];
|
||||
ASLayoutSpec *layoutSpec =
|
||||
[ASBackgroundLayoutSpec
|
||||
backgroundLayoutSpecWithChild:
|
||||
[ASStaticLayoutSpec
|
||||
staticLayoutSpecWithChildren:children]
|
||||
background:backgroundNode];
|
||||
|
||||
[self testLayoutSpec:layoutSpec sizeRange:sizeRange subnodes:subnodes identifier:identifier];
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
|
||||
- (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
return ASSizeRangeMakeExactSize(CGSizeMake(10, 42));
|
||||
return ASSizeRangeMake(CGSizeMake(10, 42));
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "ASPerformanceTestContext.h"
|
||||
#import <AsyncDisplayKit/ASTextNode.h>
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import "ASinternalHelpers.h"
|
||||
#import "ASXCTExtensions.h"
|
||||
#include "CGRect+ASConvenience.h"
|
||||
@@ -71,7 +72,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
|
||||
NSAttributedString *text = data[i % data.count];
|
||||
startMeasuring();
|
||||
node.attributedText = text;
|
||||
asdkSize = [node measure:maxSize];
|
||||
asdkSize = [node layoutThatFits:ASSizeRangeMake(CGSizeZero, maxSize)].size;
|
||||
stopMeasuring();
|
||||
}];
|
||||
ctx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);
|
||||
@@ -101,7 +102,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
|
||||
ASTextNode *node = [[ASTextNode alloc] init];
|
||||
startMeasuring();
|
||||
node.attributedText = text;
|
||||
asdkSize = [node measure:maxSize];
|
||||
asdkSize = [node layoutThatFits:ASSizeRangeMake(CGSizeZero, maxSize)].size;
|
||||
stopMeasuring();
|
||||
}];
|
||||
ctx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);
|
||||
@@ -131,7 +132,7 @@ static NSString *const kTestCaseUIKitWithReusedContext = @"UIKitReusedContext";
|
||||
ASTextNode *node = [[ASTextNode alloc] init];
|
||||
startMeasuring();
|
||||
node.attributedText = text;
|
||||
asdkSize = [node measure:maxSize];
|
||||
asdkSize = [node layoutThatFits:ASSizeRangeMake(CGSizeZero, maxSize)].size;
|
||||
stopMeasuring();
|
||||
}];
|
||||
testCtx.results[kTestCaseASDK].userInfo[@"size"] = NSStringFromCGSize(asdkSize);
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
//
|
||||
|
||||
#import "ASSnapshotTestCase.h"
|
||||
|
||||
#import <AsyncDisplayKit/AsyncDisplayKit.h>
|
||||
#import "ASLayout.h"
|
||||
|
||||
@interface ASTextNodeSnapshotTests : ASSnapshotTestCase
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
ASTextNode *textNode = [[ASTextNode alloc] init];
|
||||
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"judar"
|
||||
attributes:@{NSFontAttributeName : [UIFont italicSystemFontOfSize:24]}];
|
||||
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
|
||||
[textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
|
||||
textNode.textContainerInset = UIEdgeInsetsMake(0, 2, 0, 2);
|
||||
|
||||
ASSnapshotVerifyNode(textNode, nil);
|
||||
@@ -40,7 +40,7 @@
|
||||
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"judar judar judar judar judar judar"
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:30] }];
|
||||
|
||||
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 80))];
|
||||
[textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 80))];
|
||||
textNode.frame = CGRectMake(50, 50, textNode.calculatedSize.width, textNode.calculatedSize.height);
|
||||
textNode.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10);
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
textNode.attributedText = [[NSAttributedString alloc] initWithString:@"yolo"
|
||||
attributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:30] }];
|
||||
|
||||
[textNode measureWithSizeRange:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
|
||||
[textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX))];
|
||||
textNode.frame = CGRectMake(50, 50, textNode.calculatedSize.width, textNode.calculatedSize.height);
|
||||
textNode.textContainerInset = UIEdgeInsetsMake(5, 10, 10, 5);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#import <OCMock/OCMock.h>
|
||||
|
||||
#import <AsyncDisplayKit/ASLayout.h>
|
||||
#import <AsyncDisplayKit/ASTextNode.h>
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
@@ -111,7 +112,7 @@
|
||||
{
|
||||
for (NSInteger i = 10; i < 500; i += 50) {
|
||||
CGSize constrainedSize = CGSizeMake(i, i);
|
||||
CGSize calculatedSize = [_textNode measure:constrainedSize];
|
||||
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
XCTAssertTrue(calculatedSize.width <= constrainedSize.width, @"Calculated width (%f) should be less than or equal to constrained width (%f)", calculatedSize.width, constrainedSize.width);
|
||||
XCTAssertTrue(calculatedSize.height <= constrainedSize.height, @"Calculated height (%f) should be less than or equal to constrained height (%f)", calculatedSize.height, constrainedSize.height);
|
||||
}
|
||||
@@ -121,8 +122,8 @@
|
||||
{
|
||||
for (NSInteger i = 10; i < 500; i += 50) {
|
||||
CGSize constrainedSize = CGSizeMake(i, i);
|
||||
CGSize calculatedSize = [_textNode measure:constrainedSize];
|
||||
CGSize recalculatedSize = [_textNode measure:constrainedSize];
|
||||
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
CGSize recalculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
|
||||
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 4.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
|
||||
}
|
||||
@@ -132,8 +133,8 @@
|
||||
{
|
||||
for (CGFloat i = 10; i < 500; i *= 1.3) {
|
||||
CGSize constrainedSize = CGSizeMake(i, i);
|
||||
CGSize calculatedSize = [_textNode measure:constrainedSize];
|
||||
CGSize recalculatedSize = [_textNode measure:constrainedSize];
|
||||
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
CGSize recalculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
|
||||
XCTAssertTrue(CGSizeEqualToSizeWithIn(calculatedSize, recalculatedSize, 11.0), @"Recalculated size %@ should be same as original size %@", NSStringFromCGSize(recalculatedSize), NSStringFromCGSize(calculatedSize));
|
||||
}
|
||||
@@ -143,9 +144,9 @@
|
||||
{
|
||||
_textNode.placeholderEnabled = YES;
|
||||
|
||||
XCTAssertNoThrow([_textNode measure:CGSizeZero], @"Measure with zero size and placeholder enabled should not throw an exception");
|
||||
XCTAssertNoThrow([_textNode measure:CGSizeMake(0, 100)], @"Measure with zero width and placeholder enabled should not throw an exception");
|
||||
XCTAssertNoThrow([_textNode measure:CGSizeMake(100, 0)], @"Measure with zero height and placeholder enabled should not throw an exception");
|
||||
XCTAssertNoThrow([_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeZero)], @"Measure with zero size and placeholder enabled should not throw an exception");
|
||||
XCTAssertNoThrow([_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(0, 100))], @"Measure with zero width and placeholder enabled should not throw an exception");
|
||||
XCTAssertNoThrow([_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 0))], @"Measure with zero height and placeholder enabled should not throw an exception");
|
||||
}
|
||||
|
||||
- (void)testAccessibility
|
||||
@@ -170,7 +171,7 @@
|
||||
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
|
||||
_textNode.delegate = delegate;
|
||||
|
||||
[_textNode measure:CGSizeMake(100, 100)];
|
||||
[_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))];
|
||||
NSRange returnedLinkRange;
|
||||
NSString *returnedAttributeName;
|
||||
NSString *returnedLinkAttributeValue = [_textNode linkAttributeValueAtPoint:CGPointMake(3, 3) attributeName:&returnedAttributeName range:&returnedLinkRange];
|
||||
@@ -192,7 +193,7 @@
|
||||
ASTextNodeTestDelegate *delegate = [ASTextNodeTestDelegate new];
|
||||
_textNode.delegate = delegate;
|
||||
|
||||
CGSize calculatedSize = [_textNode measure:CGSizeMake(100, 100)];
|
||||
CGSize calculatedSize = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, CGSizeMake(100, 100))].size;
|
||||
NSRange returnedLinkRange = NSMakeRange(NSNotFound, 0);
|
||||
NSRange expectedRange = NSMakeRange(NSNotFound, 0);
|
||||
NSString *returnedAttributeName;
|
||||
@@ -218,9 +219,9 @@
|
||||
- (void)testAddingExclusionPathsShouldInvalidateAndIncreaseTheSize
|
||||
{
|
||||
CGSize constrainedSize = CGSizeMake(100, CGFLOAT_MAX);
|
||||
CGSize sizeWithoutExclusionPaths = [_textNode measure:constrainedSize];
|
||||
CGSize sizeWithoutExclusionPaths = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
_textNode.exclusionPaths = @[[UIBezierPath bezierPathWithRect:CGRectMake(50, 20, 30, 40)]];
|
||||
CGSize sizeWithExclusionPaths = [_textNode measure:constrainedSize];
|
||||
CGSize sizeWithExclusionPaths = [_textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, constrainedSize)].size;
|
||||
|
||||
XCTAssertGreaterThan(sizeWithExclusionPaths.height, sizeWithoutExclusionPaths.height, @"Setting exclusions paths should invalidate the calculated size and return a greater size");
|
||||
}
|
||||
|
||||
@@ -151,3 +151,5 @@
|
||||
#define AS_UNAVAILABLE(message)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define ASOVERLOADABLE __attribute__((overloadable))
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
CGSize size = [self.transitionNode measure:self.view.frame.size];
|
||||
CGSize size = [self.transitionNode layoutThatFits:ASSizeRangeMake(CGSizeZero, self.view.frame.size)].size;
|
||||
self.transitionNode.frame = CGRectMake(0, 20, size.width, size.height);
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,8 @@
|
||||
[self addSubnode:_likeButtonNode];
|
||||
|
||||
_muteButtonNode = [[ASButtonNode alloc] init];
|
||||
_muteButtonNode.preferredFrameSize = CGSizeMake(16.0, 22.0);
|
||||
_muteButtonNode.width = ASDimensionMakeWithPoints(16.0);
|
||||
_muteButtonNode.height = ASDimensionMakeWithPoints(22.0);
|
||||
[_muteButtonNode addTarget:self action:@selector(didTapMuteButton) forControlEvents:ASControlNodeEventTouchUpInside];
|
||||
|
||||
_videoPlayerNode = [[ASVideoPlayerNode alloc] initWithUrl:_videoModel.url loadAssetWhenNodeBecomesVisible:YES];
|
||||
@@ -86,12 +87,18 @@
|
||||
};
|
||||
}
|
||||
|
||||
- (ASLayoutSpec*)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
CGFloat fullWidth = [UIScreen mainScreen].bounds.size.width;
|
||||
_videoPlayerNode.preferredFrameSize = CGSizeMake(fullWidth, fullWidth * 9 / 16);
|
||||
_avatarNode.preferredFrameSize = CGSizeMake(AVATAR_IMAGE_HEIGHT, AVATAR_IMAGE_HEIGHT);
|
||||
_likeButtonNode.preferredFrameSize = CGSizeMake(50.0, 26.0);
|
||||
|
||||
_videoPlayerNode.width = ASDimensionMakeWithPoints(fullWidth);
|
||||
_videoPlayerNode.height = ASDimensionMakeWithPoints(fullWidth * 9 / 16);
|
||||
|
||||
_avatarNode.width = ASDimensionMakeWithPoints(AVATAR_IMAGE_HEIGHT);
|
||||
_avatarNode.height = ASDimensionMakeWithPoints(AVATAR_IMAGE_HEIGHT);
|
||||
|
||||
_likeButtonNode.width = ASDimensionMakeWithPoints(50.0);
|
||||
_likeButtonNode.height = ASDimensionMakeWithPoints(26.0);
|
||||
|
||||
ASStackLayoutSpec *headerStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
headerStack.spacing = HORIZONTAL_BUFFER;
|
||||
@@ -104,15 +111,15 @@
|
||||
ASStackLayoutSpec *bottomControlsStack = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
bottomControlsStack.spacing = HORIZONTAL_BUFFER;
|
||||
bottomControlsStack.alignItems = ASStackLayoutAlignItemsCenter;
|
||||
[bottomControlsStack setChildren:@[ _likeButtonNode]];
|
||||
bottomControlsStack.children = @[_likeButtonNode];
|
||||
|
||||
UIEdgeInsets bottomControlsInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
|
||||
UIEdgeInsets bottomControlsInsets = UIEdgeInsetsMake(HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER, HORIZONTAL_BUFFER);
|
||||
ASInsetLayoutSpec *bottomControlsInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:bottomControlsInsets child:bottomControlsStack];
|
||||
|
||||
|
||||
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
|
||||
[verticalStack setChildren:@[ headerInset, _videoPlayerNode, bottomControlsInset ]];
|
||||
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalStack.alignItems = ASStackLayoutAlignItemsStretch;
|
||||
verticalStack.children = @[headerInset, _videoPlayerNode, bottomControlsInset];
|
||||
return verticalStack;
|
||||
}
|
||||
|
||||
@@ -183,7 +190,10 @@
|
||||
|
||||
if (controls[ @(ASVideoPlayerNodeControlTypeScrubber) ]) {
|
||||
ASDisplayNode *scrubber = controls[ @(ASVideoPlayerNodeControlTypeScrubber) ];
|
||||
scrubber.preferredFrameSize = CGSizeMake(maxSize.width, 44.0);
|
||||
scrubber.height = ASDimensionMakeWithPoints(44.0);
|
||||
scrubber.minWidth = ASDimensionMakeWithPoints(0.0);
|
||||
scrubber.maxWidth = ASDimensionMakeWithPoints(maxSize.width);
|
||||
scrubber.flexGrow = YES;
|
||||
}
|
||||
|
||||
NSArray *controlBarControls = [self controlsForControlBar:controls];
|
||||
|
||||
@@ -126,7 +126,9 @@
|
||||
|
||||
// header stack
|
||||
|
||||
_userAvatarImageView.preferredFrameSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT); // constrain avatar image frame size
|
||||
// constrain avatar image frame size
|
||||
_userAvatarImageView.width = ASDimensionMakeWithPoints(USER_IMAGE_HEIGHT);
|
||||
_userAvatarImageView.height = ASDimensionMakeWithPoints(USER_IMAGE_HEIGHT);
|
||||
_photoTimeIntervalSincePostLabel.spacingBefore = HORIZONTAL_BUFFER; // to remove double spaces around spacer
|
||||
|
||||
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init]; // FIXME: long locations overflow post time - set max size?
|
||||
@@ -155,7 +157,10 @@
|
||||
|
||||
// vertical stack
|
||||
CGFloat cellWidth = constrainedSize.max.width;
|
||||
_photoImageView.preferredFrameSize = CGSizeMake(cellWidth, cellWidth); // constrain photo frame size
|
||||
|
||||
// constrain photo frame size
|
||||
_photoImageView.width = ASDimensionMakeWithPoints(cellWidth);
|
||||
_photoImageView.height = ASDimensionMakeWithPoints(cellWidth);
|
||||
|
||||
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalStack.alignItems = ASStackLayoutAlignItemsStretch; // stretch headerStack to fill horizontal space
|
||||
|
||||
@@ -115,66 +115,86 @@
|
||||
{
|
||||
#define SPACING 5
|
||||
#define HEIGHT 30
|
||||
CGSize preferredSize = CGSizeMake(constrainedSize.max.width * 0.3, HEIGHT);
|
||||
CGSize nodeSize = CGSizeMake(constrainedSize.max.width * 0.3, HEIGHT);
|
||||
|
||||
_latEditableNode.preferredFrameSize = _lonEditableNode.preferredFrameSize = preferredSize;
|
||||
_deltaLatEditableNode.preferredFrameSize = _deltaLonEditableNode.preferredFrameSize = preferredSize;
|
||||
_updateRegionButton.preferredFrameSize = _liveMapToggleButton.preferredFrameSize = preferredSize;
|
||||
[_latEditableNode setSizeWithCGSize:nodeSize];
|
||||
[_lonEditableNode setSizeWithCGSize:nodeSize];
|
||||
|
||||
[_deltaLatEditableNode setSizeWithCGSize:nodeSize];
|
||||
[_deltaLonEditableNode setSizeWithCGSize:nodeSize];
|
||||
|
||||
[_updateRegionButton setSizeWithCGSize:nodeSize];
|
||||
[_liveMapToggleButton setSizeWithCGSize:nodeSize];
|
||||
|
||||
_latEditableNode.flexGrow = _lonEditableNode.flexGrow = true;
|
||||
_deltaLatEditableNode.flexGrow = _deltaLonEditableNode.flexGrow = true;
|
||||
_updateRegionButton.flexGrow = _liveMapToggleButton.flexGrow = true;
|
||||
_latEditableNode.flexGrow = _lonEditableNode.flexGrow = YES;
|
||||
_deltaLatEditableNode.flexGrow = _deltaLonEditableNode.flexGrow = YES;
|
||||
_updateRegionButton.flexGrow = _liveMapToggleButton.flexGrow = YES;
|
||||
|
||||
_mapNode.flexGrow = true;
|
||||
_mapNode.flexGrow = YES;
|
||||
|
||||
ASStackLayoutSpec * lonlatSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_latEditableNode, _lonEditableNode]];
|
||||
ASStackLayoutSpec *lonlatSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_latEditableNode, _lonEditableNode]];
|
||||
lonlatSpec.flexGrow = true;
|
||||
|
||||
ASStackLayoutSpec * deltaLonlatSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_deltaLatEditableNode, _deltaLonEditableNode]];
|
||||
ASStackLayoutSpec *deltaLonlatSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_deltaLatEditableNode, _deltaLonEditableNode]];
|
||||
deltaLonlatSpec.flexGrow = true;
|
||||
|
||||
ASStackLayoutSpec * lonlatConfigSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[lonlatSpec, deltaLonlatSpec]];
|
||||
ASStackLayoutSpec *lonlatConfigSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[lonlatSpec, deltaLonlatSpec]];
|
||||
lonlatConfigSpec.flexGrow = true;
|
||||
|
||||
ASStackLayoutSpec * buttonsSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[_updateRegionButton, _liveMapToggleButton]];
|
||||
ASStackLayoutSpec *buttonsSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[_updateRegionButton, _liveMapToggleButton]];
|
||||
buttonsSpec.flexGrow = true;
|
||||
|
||||
ASStackLayoutSpec * dashboardSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[lonlatConfigSpec, buttonsSpec]];
|
||||
ASStackLayoutSpec *dashboardSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[lonlatConfigSpec, buttonsSpec]];
|
||||
dashboardSpec.flexGrow = true;
|
||||
|
||||
ASInsetLayoutSpec * insetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 10, 0, 10) child:dashboardSpec];
|
||||
ASInsetLayoutSpec *insetSpec =
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 10, 0, 10)
|
||||
child:dashboardSpec];
|
||||
|
||||
ASStackLayoutSpec * layoutSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[insetSpec, _mapNode ]];
|
||||
ASStackLayoutSpec *layoutSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:SPACING
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:@[insetSpec, _mapNode ]];
|
||||
return layoutSpec;
|
||||
}
|
||||
|
||||
#pragma mark - Button actions
|
||||
|
||||
-(void)updateRegion
|
||||
- (void)updateRegion
|
||||
{
|
||||
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
|
||||
f.numberStyle = NSNumberFormatterDecimalStyle;
|
||||
@@ -190,7 +210,7 @@
|
||||
_mapNode.region = region;
|
||||
}
|
||||
|
||||
-(void)toggleLiveMap
|
||||
- (void)toggleLiveMap
|
||||
{
|
||||
_mapNode.liveMap = !_mapNode.liveMap;
|
||||
NSString * const liveMapStr = [self liveMapStr];
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
self.imageNode.position = CGPointZero;
|
||||
self.imageNode.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
|
||||
[self.imageNode setSizeWithCGSize:constrainedSize.max];
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.imageNode]];
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ static const NSInteger kImageHeight = 200;
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
self.collectionNode.position = CGPointZero;
|
||||
self.collectionNode.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
|
||||
[self.collectionNode setSizeWithCGSize:constrainedSize.max];
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.collectionNode]];
|
||||
}
|
||||
|
||||
|
||||
@@ -45,8 +45,10 @@
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
self.node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.node]];
|
||||
// 100% of container
|
||||
_node.width = ASDimensionMakeWithFraction(1.0);
|
||||
_node.height = ASDimensionMakeWithFraction(1.0);
|
||||
return [ASWrapperLayoutSpec wrapperWithLayoutable:_node];
|
||||
}
|
||||
|
||||
#pragma mark - <ASCollectionDataSource, ASCollectionDelegate>
|
||||
|
||||
@@ -37,9 +37,7 @@ static UIColor *OverViewASPagerNodeRandomColor() {
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:constrainedSize.max];
|
||||
return [ASLayout layoutWithLayoutable:self size:constrainedSize.max];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -69,8 +67,10 @@ static UIColor *OverViewASPagerNodeRandomColor() {
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
_node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_node]];
|
||||
// 100% of container
|
||||
_node.width = ASDimensionMakeWithFraction(1.0);
|
||||
_node.height = ASDimensionMakeWithFraction(1.0);
|
||||
return [ASWrapperLayoutSpec wrapperWithLayoutable:_node];
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode
|
||||
|
||||
@@ -42,8 +42,10 @@
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
_node.sizeRange = ASRelativeSizeRangeMakeWithExactCGSize(constrainedSize.max);
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[_node]];
|
||||
// 100% of container
|
||||
_node.width = ASDimensionMakeWithFraction(1.0);
|
||||
_node.height = ASDimensionMakeWithFraction(1.0);
|
||||
return [ASWrapperLayoutSpec wrapperWithLayoutable:_node];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
BOOL hasDescription = self.descriptionNode.attributedText.length > 0;
|
||||
|
||||
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalStackLayoutSpec.alignItems = ASStackLayoutAlignItemsStart;
|
||||
verticalStackLayoutSpec.spacing = 5.0;
|
||||
verticalStackLayoutSpec.children = hasDescription ? @[self.titleNode, self.descriptionNode] : @[self.titleNode];
|
||||
|
||||
@@ -203,8 +204,11 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
|
||||
#pragma mark ASImageNode
|
||||
ASImageNode *imageNode = [ASImageNode new];
|
||||
imageNode.image = [UIImage imageNamed:@"image"];
|
||||
imageNode.preferredFrameSize = CGSizeMake(imageNode.image.size.width / 7, imageNode.image.size.height / 7);
|
||||
imageNode.image = [UIImage imageNamed:@"image.jpg"];
|
||||
|
||||
CGSize imageNetworkImageNodeSize = (CGSize){imageNode.image.size.width / 7, imageNode.image.size.height / 7};
|
||||
|
||||
[imageNode setSizeWithCGSize:imageNetworkImageNodeSize];
|
||||
|
||||
parentNode = [self centeringParentNodeWithChild:imageNode];
|
||||
parentNode.entryTitle = @"ASImageNode";
|
||||
@@ -214,7 +218,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
#pragma mark ASNetworkImageNode
|
||||
ASNetworkImageNode *networkImageNode = [ASNetworkImageNode new];
|
||||
networkImageNode.URL = [NSURL URLWithString:@"http://i.imgur.com/FjOR9kX.jpg"];
|
||||
networkImageNode.preferredFrameSize = CGSizeMake(imageNode.image.size.width / 7, imageNode.image.size.height / 7);
|
||||
[networkImageNode setSizeWithCGSize:imageNetworkImageNodeSize];
|
||||
|
||||
parentNode = [self centeringParentNodeWithChild:networkImageNode];
|
||||
parentNode.entryTitle = @"ASNetworkImageNode";
|
||||
@@ -223,7 +227,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
|
||||
#pragma mark ASMapNode
|
||||
ASMapNode *mapNode = [ASMapNode new];
|
||||
mapNode.preferredFrameSize = CGSizeMake(300.0, 300.0);
|
||||
[mapNode setSizeWithCGSize:(CGSize){300.0, 300.0}];
|
||||
|
||||
// San Francisco
|
||||
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194);
|
||||
@@ -236,7 +240,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
|
||||
#pragma mark ASVideoNode
|
||||
ASVideoNode *videoNode = [ASVideoNode new];
|
||||
videoNode.preferredFrameSize = CGSizeMake(300.0, 400.0);
|
||||
[videoNode setSizeWithCGSize:(CGSize){300.0, 400.0}];
|
||||
|
||||
AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://www.w3schools.com/html/mov_bbb.mp4"]];
|
||||
videoNode.asset = asset;
|
||||
@@ -250,7 +254,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
UIImage *scrollNodeImage = [UIImage imageNamed:@"image"];
|
||||
|
||||
ASScrollNode *scrollNode = [ASScrollNode new];
|
||||
scrollNode.preferredFrameSize = CGSizeMake(300.0, 400.0);
|
||||
[scrollNode setSizeWithCGSize:(CGSize){300.0, 400.0}];
|
||||
|
||||
UIScrollView *scrollNodeView = scrollNode.view;
|
||||
[scrollNodeView addSubview:[[UIImageView alloc] initWithImage:scrollNodeImage]];
|
||||
@@ -391,6 +395,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
parentNode.entryDescription = @"Is based on a simplified version of CSS flexbox. It allows you to stack components vertically or horizontally and specify how they should be flexed and aligned to fit in the available space.";
|
||||
parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) {
|
||||
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec verticalStackLayoutSpec];
|
||||
verticalStackLayoutSpec.alignItems = ASStackLayoutAlignItemsStart;
|
||||
verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3];
|
||||
return verticalStackLayoutSpec;
|
||||
};
|
||||
@@ -401,17 +406,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
|
||||
#pragma mark Horizontal ASStackLayoutSpec
|
||||
childNode1 = [ASDisplayNode new];
|
||||
childNode1.preferredFrameSize = CGSizeMake(10.0, 20);
|
||||
[childNode1 setSizeWithCGSize:(CGSize){10.0, 20.0}];
|
||||
childNode1.flexGrow = YES;
|
||||
childNode1.backgroundColor = [UIColor greenColor];
|
||||
|
||||
childNode2 = [ASDisplayNode new];
|
||||
childNode2.preferredFrameSize = CGSizeMake(10.0, 20.0);
|
||||
[childNode2 setSizeWithCGSize:(CGSize){10.0, 20.0}];
|
||||
childNode2.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||
childNode2.backgroundColor = [UIColor blueColor];
|
||||
|
||||
childNode3 = [ASDisplayNode new];
|
||||
childNode3.preferredFrameSize = CGSizeMake(10.0, 20.0);
|
||||
[childNode3 setSizeWithCGSize:(CGSize){10.0, 20.0}];
|
||||
childNode3.backgroundColor = [UIColor yellowColor];
|
||||
|
||||
parentNode = [self parentNodeWithChild:childNode];
|
||||
@@ -420,20 +425,17 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
parentNode.sizeThatFitsBlock = ^ASLayoutSpec *(ASSizeRange constrainedSize) {
|
||||
|
||||
// Create stack alyout spec to layout children
|
||||
ASStackLayoutSpec *verticalStackLayoutSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
verticalStackLayoutSpec.children = @[childNode1, childNode2, childNode3];
|
||||
verticalStackLayoutSpec.spacing = 5.0; // Spacing between children
|
||||
ASStackLayoutSpec *horizontalStackSpec = [ASStackLayoutSpec horizontalStackLayoutSpec];
|
||||
horizontalStackSpec.alignItems = ASStackLayoutAlignItemsStart;
|
||||
horizontalStackSpec.children = @[childNode1, childNode2, childNode3];
|
||||
horizontalStackSpec.spacing = 5.0; // Spacing between children
|
||||
|
||||
// Layout the stack layout with 100% width and 100% height of the parent node
|
||||
ASRelativeSizeRange sizeRange = ASRelativeSizeRangeMakeWithExactRelativeDimensions(ASRelativeDimensionMakeWithFraction(1),
|
||||
ASRelativeDimensionMakeWithFraction(1));
|
||||
verticalStackLayoutSpec.sizeRange = sizeRange;
|
||||
|
||||
// Wrap the static stack layout in a static spec so it will grow to the whole parent node size
|
||||
ASStaticLayoutSpec *staticLayoutSpec = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[verticalStackLayoutSpec]];
|
||||
horizontalStackSpec.height = ASDimensionMakeWithFraction(1.0);
|
||||
horizontalStackSpec.width = ASDimensionMakeWithFraction(1.0);
|
||||
|
||||
// Add a bit of inset
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0.0, 5.0, 0.0, 5.0) child:staticLayoutSpec];
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0.0, 5.0, 0.0, 5.0) child:horizontalStackSpec];
|
||||
};
|
||||
[parentNode addSubnode:childNode1];
|
||||
[parentNode addSubnode:childNode2];
|
||||
@@ -465,7 +467,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
- (OverviewDisplayNodeWithSizeBlock *)parentNodeWithChild:(ASDisplayNode *)child
|
||||
{
|
||||
OverviewDisplayNodeWithSizeBlock *parentNode = [OverviewDisplayNodeWithSizeBlock new];
|
||||
parentNode.preferredFrameSize = CGSizeMake(100, 100);
|
||||
[parentNode setSizeWithCGSize:(CGSize){100, 100}];
|
||||
parentNode.backgroundColor = [UIColor redColor];
|
||||
return parentNode;
|
||||
}
|
||||
@@ -489,7 +491,7 @@ typedef ASLayoutSpec *(^OverviewDisplayNodeSizeThatFitsBlock)(ASSizeRange constr
|
||||
- (ASDisplayNode *)childNode
|
||||
{
|
||||
ASDisplayNode *childNode = [ASDisplayNode new];
|
||||
childNode.preferredFrameSize = CGSizeMake(50, 50);
|
||||
[childNode setSizeWithCGSize:(CGSize){50, 50}];
|
||||
childNode.backgroundColor = [UIColor blueColor];
|
||||
return childNode;
|
||||
}
|
||||
|
||||
@@ -51,12 +51,11 @@
|
||||
|
||||
// Center node frame
|
||||
CGRect bounds = self.view.bounds;
|
||||
CGSize nodeSize = self.node.preferredFrameSize;
|
||||
if (CGSizeEqualToSize(nodeSize, CGSizeZero)) {
|
||||
nodeSize = self.view.bounds.size;
|
||||
}
|
||||
self.node.frame = CGRectMake(CGRectGetMidX(bounds) - (nodeSize.width / 2.0), CGRectGetMidY(bounds) - (nodeSize.height / 2.0), nodeSize.width, nodeSize.height);
|
||||
[self.node measure:self.node.bounds.size];
|
||||
CGSize nodeSize = [self.node layoutThatFits:ASSizeRangeMake(CGSizeZero, bounds.size)].size;
|
||||
self.node.frame = CGRectMake(CGRectGetMidX(bounds) - (nodeSize.width / 2.0),
|
||||
CGRectGetMidY(bounds) - (nodeSize.height / 2.0),
|
||||
nodeSize.width,
|
||||
nodeSize.height);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -107,7 +107,8 @@ const CGFloat kSoldOutGBHeight = 50.0;
|
||||
self.soldOutLabelFlat.layerBacked = YES;
|
||||
|
||||
self.soldOutLabelBackground = [[ASDisplayNode alloc] init];
|
||||
self.soldOutLabelBackground.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(1), ASRelativeDimensionMakeWithPoints(kSoldOutGBHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(1), ASRelativeDimensionMakeWithPoints(kSoldOutGBHeight)));
|
||||
self.soldOutLabelBackground.width = ASDimensionMakeWithFraction(1.0);
|
||||
self.soldOutLabelBackground.height = ASDimensionMakeWithPoints(kSoldOutGBHeight);
|
||||
self.soldOutLabelBackground.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.9];
|
||||
self.soldOutLabelBackground.flexGrow = YES;
|
||||
self.soldOutLabelBackground.layerBacked = YES;
|
||||
@@ -289,7 +290,7 @@ const CGFloat kSoldOutGBHeight = 50.0;
|
||||
ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:self.dealImageView];
|
||||
|
||||
self.badge.layoutPosition = CGPointMake(0, constrainedSize.max.height - kFixedLabelsAreaHeight - kBadgeHeight);
|
||||
self.badge.sizeRange = ASRelativeSizeRangeMake(ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(0), ASRelativeDimensionMakeWithPoints(kBadgeHeight)), ASRelativeSizeMake(ASRelativeDimensionMakeWithFraction(1), ASRelativeDimensionMakeWithPoints(kBadgeHeight)));
|
||||
self.badge.height = ASDimensionMakeWithPoints(kBadgeHeight);
|
||||
ASStaticLayoutSpec *badgePosition = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.badge]];
|
||||
|
||||
ASOverlayLayoutSpec *badgeOverImage = [ASOverlayLayoutSpec overlayLayoutSpecWithChild:imagePlace overlay:badgePosition];
|
||||
@@ -300,8 +301,7 @@ const CGFloat kSoldOutGBHeight = 50.0;
|
||||
|
||||
- (ASLayoutSpec *)soldOutLabelSpec {
|
||||
ASCenterLayoutSpec *centerSoldOutLabel = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionMinimumXY child:self.soldOutLabelFlat];
|
||||
ASStaticLayoutSpec *soldOutBG = [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[self.soldOutLabelBackground]];
|
||||
ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:soldOutBG];
|
||||
ASCenterLayoutSpec *centerSoldOut = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:self.soldOutLabelBackground];
|
||||
ASBackgroundLayoutSpec *soldOutLabelOverBackground = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:centerSoldOutLabel background:centerSoldOut];
|
||||
return soldOutLabelOverBackground;
|
||||
}
|
||||
|
||||
@@ -53,9 +53,8 @@ static CGFloat kFixedHeight = 200.0f;
|
||||
[spinner startAnimating];
|
||||
return spinner;
|
||||
}];
|
||||
_loadingSpinner.preferredFrameSize = CGSizeMake(50, 50);
|
||||
|
||||
|
||||
[_loadingSpinner setSizeWithCGSize:CGSizeMake(50, 50)];
|
||||
|
||||
// add it as a subnode, and we're done
|
||||
[self addSubnode:_loadingSpinner];
|
||||
|
||||
|
||||
@@ -34,17 +34,9 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (CGSize)calculateSizeThatFits:(CGSize)constrainedSize
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
[_imageNode measure:constrainedSize];
|
||||
return constrainedSize;
|
||||
}
|
||||
|
||||
- (void)layout
|
||||
{
|
||||
[super layout];
|
||||
|
||||
_imageNode.frame = CGRectMake(0, 0, _imageNode.calculatedSize.width, _imageNode.calculatedSize.height);
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_imageNode];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -64,6 +64,7 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
- (void)didLoad
|
||||
{
|
||||
[super didLoad];
|
||||
|
||||
_collectionNode.view.asyncDelegate = self;
|
||||
_collectionNode.view.asyncDataSource = self;
|
||||
}
|
||||
@@ -75,21 +76,22 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
|
||||
- (ASCellNodeBlock)collectionView:(ASCollectionView *)collectionView nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
CGSize elementSize = _elementSize;
|
||||
return ^{
|
||||
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
|
||||
elementNode.preferredFrameSize = _elementSize;
|
||||
[elementNode setSizeWithCGSize:elementSize];
|
||||
return elementNode;
|
||||
};
|
||||
}
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
_collectionNode.preferredFrameSize = CGSizeMake(self.bounds.size.width, _elementSize.height);
|
||||
CGSize collectionNodeSize = CGSizeMake(constrainedSize.max.width, _elementSize.height);
|
||||
[_collectionNode setSizeWithCGSize:collectionNodeSize];
|
||||
|
||||
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
|
||||
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, 0.0, kOuterPadding, 0.0);
|
||||
insetSpec.child = _collectionNode;
|
||||
|
||||
return insetSpec;
|
||||
}
|
||||
|
||||
|
||||
@@ -68,13 +68,11 @@
|
||||
_tableView.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark ASTableView.
|
||||
#pragma mark - ASTableView.
|
||||
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
HorizontalScrollCellNode *node = [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)];
|
||||
return node;
|
||||
return [[HorizontalScrollCellNode alloc] initWithElementSize:CGSizeMake(100, 100)];
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
|
||||
@@ -98,8 +98,7 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
|
||||
// lorem ipsum text, plus some nice styling
|
||||
_textNode = [[ASTextNode alloc] init];
|
||||
_textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum]
|
||||
attributes:[self textStyle]];
|
||||
_textNode.attributedText = [[NSAttributedString alloc] initWithString:[self kittyIpsum] attributes:[self textStyle]];
|
||||
[self addSubnode:_textNode];
|
||||
|
||||
// hairline cell separator
|
||||
@@ -134,27 +133,36 @@ static const CGFloat kInnerPadding = 10.0f;
|
||||
style.paragraphSpacing = 0.5 * font.lineHeight;
|
||||
style.hyphenationFactor = 1.0;
|
||||
|
||||
return @{ NSFontAttributeName: font,
|
||||
NSParagraphStyleAttributeName: style,
|
||||
ASTextNodeWordKerningAttributeName : @.5};
|
||||
return @{
|
||||
NSFontAttributeName: font,
|
||||
NSParagraphStyleAttributeName: style,
|
||||
ASTextNodeWordKerningAttributeName : @.5
|
||||
};
|
||||
}
|
||||
|
||||
#if UseAutomaticLayout
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
_imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize);
|
||||
// Set an intrinsic size for the image node
|
||||
CGSize imageSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize);
|
||||
[_imageNode setSizeWithCGSize:imageSize];
|
||||
|
||||
// Shrink the text node in case the image + text gonna be too wide
|
||||
_textNode.flexShrink = YES;
|
||||
|
||||
// Configure stack
|
||||
ASStackLayoutSpec *stackLayoutSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:kInnerPadding
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStart
|
||||
children:_swappedTextAndImage ? @[_textNode, _imageNode] : @[_imageNode, _textNode]];
|
||||
|
||||
ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init];
|
||||
stackSpec.direction = ASStackLayoutDirectionHorizontal;
|
||||
stackSpec.spacing = kInnerPadding;
|
||||
[stackSpec setChildren:!_swappedTextAndImage ? @[_imageNode, _textNode] : @[_textNode, _imageNode]];
|
||||
|
||||
ASInsetLayoutSpec *insetSpec = [[ASInsetLayoutSpec alloc] init];
|
||||
insetSpec.insets = UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding);
|
||||
insetSpec.child = stackSpec;
|
||||
|
||||
return insetSpec;
|
||||
// Add inset
|
||||
return [ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:UIEdgeInsetsMake(kOuterPadding, kOuterPadding, kOuterPadding, kOuterPadding)
|
||||
child:stackLayoutSpec];
|
||||
}
|
||||
|
||||
// With box model, you don't need to override this method, unless you want to add custom logic.
|
||||
|
||||
@@ -23,9 +23,7 @@
|
||||
|
||||
- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
return [ASLayout layoutWithLayoutableObject:self
|
||||
constrainedSizeRange:constrainedSize
|
||||
size:constrainedSize.max];
|
||||
return [ASLayout layoutWithLayoutable:self size:constrainedSize.max];
|
||||
}
|
||||
|
||||
- (void)fetchData
|
||||
|
||||
@@ -52,13 +52,19 @@
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:6.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:@[self.iconNode, self.countNode]];
|
||||
ASStackLayoutSpec *mainStack =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:6.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_iconNode, _countNode]];
|
||||
|
||||
// set sizeRange to make width fixed to 60
|
||||
ASRelativeSize min = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(0.0));
|
||||
ASRelativeSize max = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(40.0));
|
||||
mainStack.sizeRange = ASRelativeSizeRangeMake(min,max);
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[mainStack]];
|
||||
// Adjust size
|
||||
mainStack.minWidth = ASDimensionMakeWithPoints(60.0);
|
||||
mainStack.maxHeight = ASDimensionMakeWithPoints(40.0);
|
||||
|
||||
return mainStack;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -66,13 +66,18 @@
|
||||
|
||||
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
{
|
||||
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:6.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:@[_iconNode, _countNode]];
|
||||
|
||||
// set sizeRange to make width fixed to 60
|
||||
ASRelativeSize min = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(0.0));
|
||||
ASRelativeSize max = ASRelativeSizeMake(ASRelativeDimensionMakeWithPoints(60.0), ASRelativeDimensionMakeWithPoints(40.0));
|
||||
mainStack.sizeRange = ASRelativeSizeRangeMake(min, max);
|
||||
return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[mainStack]];
|
||||
ASStackLayoutSpec *mainStack =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:6.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_iconNode, _countNode]];
|
||||
|
||||
mainStack.minWidth = ASDimensionMakeWithPoints(60.0);
|
||||
mainStack.maxHeight = ASDimensionMakeWithPoints(40.0);
|
||||
|
||||
return mainStack;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#import "TextStyles.h"
|
||||
#import "LikesNode.h"
|
||||
#import "CommentsNode.h"
|
||||
#import "ASRelativeSize.h"
|
||||
|
||||
#define PostNodeDividerColor [UIColor lightGrayColor]
|
||||
|
||||
@@ -136,7 +137,8 @@
|
||||
// User pic
|
||||
_avatarNode = [[ASNetworkImageNode alloc] init];
|
||||
_avatarNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor();
|
||||
_avatarNode.preferredFrameSize = CGSizeMake(44, 44);
|
||||
_avatarNode.width = ASDimensionMakeWithPoints(44);
|
||||
_avatarNode.height = ASDimensionMakeWithPoints(44);
|
||||
_avatarNode.cornerRadius = 22.0;
|
||||
_avatarNode.URL = [NSURL URLWithString:_post.photo];
|
||||
_avatarNode.imageModificationBlock = ^UIImage *(UIImage *image) {
|
||||
@@ -214,7 +216,10 @@
|
||||
// NOTE: This inset is not actually required by the layout, but is an example of the upward propogation of layoutable
|
||||
// properties. Specifically, .flexGrow from the child is transferred to the inset spec so they can expand together.
|
||||
// Without this capability, it would be required to set insetSpacer.flexGrow = YES;
|
||||
ASInsetLayoutSpec *insetSpacer = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 0, 0, 0) child:spacer];
|
||||
ASInsetLayoutSpec *insetSpacer =
|
||||
[ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 0, 0, 0)
|
||||
child:spacer];
|
||||
|
||||
// Horizontal stack for name, username, via icon and time
|
||||
NSMutableArray *layoutSpecChildren = [@[_nameNode, _usernameNode, insetSpacer] mutableCopy];
|
||||
@@ -223,11 +228,23 @@
|
||||
}
|
||||
[layoutSpecChildren addObject:_timeNode];
|
||||
|
||||
ASStackLayoutSpec *nameStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:5.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:layoutSpecChildren];
|
||||
ASStackLayoutSpec *nameStack =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:5.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:layoutSpecChildren];
|
||||
nameStack.alignSelf = ASStackLayoutAlignSelfStretch;
|
||||
|
||||
// bottom controls horizontal stack
|
||||
ASStackLayoutSpec *controlsStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:10 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsCenter children:@[_likesNode, _commentsNode, _optionsNode]];
|
||||
ASStackLayoutSpec *controlsStack =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:10
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsCenter
|
||||
children:@[_likesNode, _commentsNode, _optionsNode]];
|
||||
|
||||
// Add more gaps for control line
|
||||
controlsStack.spacingAfter = 3.0;
|
||||
@@ -237,26 +254,46 @@
|
||||
[mainStackContent addObject:nameStack];
|
||||
[mainStackContent addObject:_postNode];
|
||||
|
||||
if (![_post.media isEqualToString:@""]) {
|
||||
CGFloat imageRatio = (_mediaNode.image != nil ? _mediaNode.image.size.height / _mediaNode.image.size.width : 0.5);
|
||||
ASRatioLayoutSpec *imagePlace = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:imageRatio child:_mediaNode];
|
||||
imagePlace.spacingAfter = 3.0;
|
||||
imagePlace.spacingBefore = 3.0;
|
||||
|
||||
[mainStackContent addObject:imagePlace];
|
||||
|
||||
if (![_post.media isEqualToString:@""]){
|
||||
|
||||
// Only add the media node if an image is present
|
||||
if (_mediaNode.image != nil) {
|
||||
CGFloat imageRatio = (_mediaNode.image != nil ? _mediaNode.image.size.height / _mediaNode.image.size.width : 0.5);
|
||||
ASRatioLayoutSpec *imagePlace =
|
||||
[ASRatioLayoutSpec
|
||||
ratioLayoutSpecWithRatio:imageRatio
|
||||
child:_mediaNode];
|
||||
imagePlace.spacingAfter = 3.0;
|
||||
imagePlace.spacingBefore = 3.0;
|
||||
|
||||
[mainStackContent addObject:imagePlace];
|
||||
}
|
||||
}
|
||||
[mainStackContent addObject:controlsStack];
|
||||
|
||||
// Vertical spec of cell main content
|
||||
ASStackLayoutSpec *contentSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:8.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:mainStackContent];
|
||||
contentSpec.alignItems = ASStackLayoutAlignSelfStretch;
|
||||
ASStackLayoutSpec *contentSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
|
||||
spacing:8.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStretch
|
||||
children:mainStackContent];
|
||||
contentSpec.flexShrink = YES;
|
||||
|
||||
// Horizontal spec for avatar
|
||||
ASStackLayoutSpec *avatarContentSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal spacing:8.0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[_avatarNode, contentSpec]];
|
||||
ASStackLayoutSpec *avatarContentSpec =
|
||||
[ASStackLayoutSpec
|
||||
stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
|
||||
spacing:8.0
|
||||
justifyContent:ASStackLayoutJustifyContentStart
|
||||
alignItems:ASStackLayoutAlignItemsStart
|
||||
children:@[_avatarNode, contentSpec]];
|
||||
|
||||
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10) child:avatarContentSpec];
|
||||
return [ASInsetLayoutSpec
|
||||
insetLayoutSpecWithInsets:UIEdgeInsetsMake(10, 10, 10, 10)
|
||||
child:avatarContentSpec];
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.tableView = [[ASTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain asyncDataFetching:YES];
|
||||
self.tableView = [[ASTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
|
||||
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; // SocialAppNode has its own separator
|
||||
self.tableView.asyncDataSource = self;
|
||||
|
||||
@@ -25,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
|
||||
let window = UIWindow(frame: UIScreen.mainScreen().bounds)
|
||||
window.backgroundColor = UIColor.whiteColor()
|
||||
window.rootViewController = ViewController()
|
||||
window.rootViewController = UINavigationController(rootViewController: ViewController());
|
||||
window.makeKeyAndVisible()
|
||||
self.window = window
|
||||
return true
|
||||
|
||||
@@ -54,7 +54,8 @@ final class SpinnerNode: ASDisplayNode {
|
||||
|
||||
override init() {
|
||||
super.init(viewBlock: { UIActivityIndicatorView(activityIndicatorStyle: .Gray) }, didLoadBlock: nil)
|
||||
preferredFrameSize.height = 32
|
||||
|
||||
size.minHeight = ASDimensionMakeWithPoints(44.0)
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
|
||||
@@ -127,14 +127,15 @@ final class ViewController: ASViewController, ASTableDataSource, ASTableDelegate
|
||||
/// (Pretend) fetches some new items and calls the
|
||||
/// completion handler on the main thread.
|
||||
private static func fetchDataWithCompletion(completion: (Int) -> Void) {
|
||||
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSTimeInterval(NSEC_PER_SEC) * 0.5))
|
||||
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(NSTimeInterval(NSEC_PER_SEC) * 1.0))
|
||||
dispatch_after(time, dispatch_get_main_queue()) {
|
||||
let resultCount = Int(arc4random_uniform(20))
|
||||
completion(resultCount)
|
||||
}
|
||||
}
|
||||
|
||||
private static func handleAction(action: Action, var fromState state: State) -> State {
|
||||
private static func handleAction(action: Action, fromState state: State) -> State {
|
||||
var state = state
|
||||
switch action {
|
||||
case .BeginBatchFetch:
|
||||
state.fetchingMore = true
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init];
|
||||
elementNode.preferredFrameSize = _elementSize;
|
||||
[elementNode setSizeWithCGSize:_elementSize];
|
||||
elementNode.indexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:_pageNumber];
|
||||
return elementNode;
|
||||
}
|
||||
|
||||
@@ -71,17 +71,11 @@
|
||||
_indexPathTextNode.attributedText = [[NSAttributedString alloc] initWithString:[indexPath description] attributes:nil];
|
||||
}
|
||||
|
||||
//- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
|
||||
//{
|
||||
// ASStackLayoutSpec *stackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical spacing:0 justifyContent:ASStackLayoutJustifyContentStart alignItems:ASStackLayoutAlignItemsStart children:@[_indexPathTextNode]];
|
||||
// stackSpec.flexGrow = YES;
|
||||
// return stackSpec;
|
||||
//}
|
||||
|
||||
- (void)layout
|
||||
{
|
||||
_indexPathTextNode.frame = self.bounds;
|
||||
[super layout];
|
||||
|
||||
_indexPathTextNode.frame = self.bounds;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user