diff --git a/AsyncDisplayKit.xcodeproj/project.pbxproj b/AsyncDisplayKit.xcodeproj/project.pbxproj index fd6dab77c6..78394febff 100644 --- a/AsyncDisplayKit.xcodeproj/project.pbxproj +++ b/AsyncDisplayKit.xcodeproj/project.pbxproj @@ -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 = ""; }; 69708BA41D76386D005C3CF9 /* ASEqualityHashHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASEqualityHashHelpers.h; path = TextKit/ASEqualityHashHelpers.h; sourceTree = ""; }; 69708BA51D76386D005C3CF9 /* ASEqualityHashHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASEqualityHashHelpers.mm; path = TextKit/ASEqualityHashHelpers.mm; sourceTree = ""; }; + 6959433C1D70815300B0EE1F /* ASDisplayNodeLayout.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASDisplayNodeLayout.mm; sourceTree = ""; }; + 6959433D1D70815300B0EE1F /* ASDisplayNodeLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASDisplayNodeLayout.h; sourceTree = ""; }; 697B31591CFE4B410049936F /* ASEditableTextNodeTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASEditableTextNodeTests.m; sourceTree = ""; }; 697C0DE11CF38F28001DE0D4 /* ASLayoutValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ASLayoutValidation.h; path = AsyncDisplayKit/Layout/ASLayoutValidation.h; sourceTree = ""; }; 697C0DE21CF38F28001DE0D4 /* ASLayoutValidation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ASLayoutValidation.mm; path = AsyncDisplayKit/Layout/ASLayoutValidation.mm; sourceTree = ""; }; @@ -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 = ""; @@ -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 */, diff --git a/AsyncDisplayKit/ASButtonNode.mm b/AsyncDisplayKit/ASButtonNode.mm index d00edf25ad..64e6ca5a33 100644 --- a/AsyncDisplayKit/ASButtonNode.mm +++ b/AsyncDisplayKit/ASButtonNode.mm @@ -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]; diff --git a/AsyncDisplayKit/ASDisplayNode+Deprecated.h b/AsyncDisplayKit/ASDisplayNode+Deprecated.h index 80c84a770a..737116d4dd 100644 --- a/AsyncDisplayKit/ASDisplayNode+Deprecated.h +++ b/AsyncDisplayKit/ASDisplayNode+Deprecated.h @@ -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. * diff --git a/AsyncDisplayKit/ASDisplayNode+Subclasses.h b/AsyncDisplayKit/ASDisplayNode+Subclasses.h index 16932feb64..cce25a4c21 100644 --- a/AsyncDisplayKit/ASDisplayNode+Subclasses.h +++ b/AsyncDisplayKit/ASDisplayNode+Subclasses.h @@ -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; diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index 74ed9578f2..98970da00c 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -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) /** @@ -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 /* diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index 40fa4c6cc3..3f8604ff9d 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -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(); + _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( + [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( + 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)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 *removedSubnodes = [context removedSubnodes]; NSMutableArray *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)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) diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index 706c09c4c4..da6ce21b79 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -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}; diff --git a/AsyncDisplayKit/ASMapNode.mm b/AsyncDisplayKit/ASMapNode.mm index 1e3ec85786..d188223159 100644 --- a/AsyncDisplayKit/ASMapNode.mm +++ b/AsyncDisplayKit/ASMapNode.mm @@ -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 diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index 8dc5052495..842a684273 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -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]; } diff --git a/AsyncDisplayKit/ASVideoNode.mm b/AsyncDisplayKit/ASVideoNode.mm index 8ba56f1dfc..5395b3ed1d 100644 --- a/AsyncDisplayKit/ASVideoNode.mm +++ b/AsyncDisplayKit/ASVideoNode.mm @@ -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; diff --git a/AsyncDisplayKit/ASVideoPlayerNode.mm b/AsyncDisplayKit/ASVideoPlayerNode.mm index f105aa4b2b..467798884d 100644 --- a/AsyncDisplayKit/ASVideoPlayerNode.mm +++ b/AsyncDisplayKit/ASVideoPlayerNode.mm @@ -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; diff --git a/AsyncDisplayKit/ASViewController.mm b/AsyncDisplayKit/ASViewController.mm index fd9b67c9e0..4b3090677d 100644 --- a/AsyncDisplayKit/ASViewController.mm +++ b/AsyncDisplayKit/ASViewController.mm @@ -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]; } } diff --git a/AsyncDisplayKit/AsyncDisplayKit+Debug.m b/AsyncDisplayKit/AsyncDisplayKit+Debug.m index 844f52e496..ae32b38df5 100644 --- a/AsyncDisplayKit/AsyncDisplayKit+Debug.m +++ b/AsyncDisplayKit/AsyncDisplayKit+Debug.m @@ -15,6 +15,7 @@ #import "ASDisplayNodeExtras.h" #import "ASWeakSet.h" #import "UIImage+ASConvenience.h" +#import #import #import #import @@ -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; diff --git a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m index d548d937ad..4fa8abdb2d 100644 --- a/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m +++ b/AsyncDisplayKit/Details/ASCollectionViewFlowLayoutInspector.m @@ -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); } diff --git a/AsyncDisplayKit/Details/ASDataController.mm b/AsyncDisplayKit/Details/ASDataController.mm index e6ec2b763d..b0f7ece2d1 100644 --- a/AsyncDisplayKit/Details/ASDataController.mm +++ b/AsyncDisplayKit/Details/ASDataController.mm @@ -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; } diff --git a/AsyncDisplayKit/Details/ASEnvironment.h b/AsyncDisplayKit/Details/ASEnvironment.h index 4dad9fde9f..120ef69f4d 100644 --- a/AsyncDisplayKit/Details/ASEnvironment.h +++ b/AsyncDisplayKit/Details/ASEnvironment.h @@ -12,7 +12,6 @@ #import #import -#import @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; diff --git a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.h b/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.h index 0d2d583771..0527f98269 100644 --- a/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.h +++ b/AsyncDisplayKit/Details/Transactions/_ASAsyncTransactionContainer.h @@ -8,7 +8,6 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import #import diff --git a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm index 99ec4061fe..f90acb36cd 100644 --- a/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm @@ -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)background diff --git a/AsyncDisplayKit/Layout/ASDimension.h b/AsyncDisplayKit/Layout/ASDimension.h index 79e7e2f64b..d1710ca29e 100644 --- a/AsyncDisplayKit/Layout/ASDimension.h +++ b/AsyncDisplayKit/Layout/ASDimension.h @@ -11,58 +11,203 @@ #pragma once #import #import +#import -/** 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: + * + */ +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 diff --git a/AsyncDisplayKit/Layout/ASDimension.mm b/AsyncDisplayKit/Layout/ASDimension.mm index a60b6b4ba9..6f5f232d96 100644 --- a/AsyncDisplayKit/Layout/ASDimension.mm +++ b/AsyncDisplayKit/Layout/ASDimension.mm @@ -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: + @"", + 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:@"", NSStringFromCGSize(sizeRange.min), NSStringFromCGSize(sizeRange.max)]; } + + +#pragma mark - Deprecated + +ASSizeRange ASSizeRangeMakeExactSize(CGSize size) +{ + return ASSizeRangeMake(size); +} diff --git a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm index 9a6a32707c..3c7f49857d 100644 --- a/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm @@ -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 diff --git a/AsyncDisplayKit/Layout/ASLayout.h b/AsyncDisplayKit/Layout/ASLayout.h index 31465987fb..c5f66b21d2 100644 --- a/AsyncDisplayKit/Layout/ASLayout.h +++ b/AsyncDisplayKit/Layout/ASLayout.h @@ -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 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 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 layoutableObject; +@property (nonatomic, weak, readonly) id 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 *sublayouts; - -/** - * Mark the layout dirty for future regeneration. - */ -@property (nonatomic, getter=isDirty) BOOL dirty; +@property (nonatomic, copy, readonly) NSArray *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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - position:(CGPoint)position - sublayouts:(NSArray *)sublayouts NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithLayoutable:(id)layoutable + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - position:(CGPoint)position - sublayouts:(nullable NSArray *)sublayouts; ++ (instancetype)layoutWithLayoutable:(id)layoutable + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - sublayouts:(nullable NSArray *)sublayouts; ++ (instancetype)layoutWithLayoutable:(id)layoutable + size:(CGSize)size + sublayouts:(nullable NSArray *)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)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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - sublayouts:(nullable NSArray *)sublayouts; - ++ (instancetype)layoutWithLayoutable:(id)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 diff --git a/AsyncDisplayKit/Layout/ASLayout.mm b/AsyncDisplayKit/Layout/ASLayout.mm index 575f593ed6..42dc5ca395 100644 --- a/AsyncDisplayKit/Layout/ASLayout.mm +++ b/AsyncDisplayKit/Layout/ASLayout.mm @@ -51,38 +51,37 @@ static inline NSString * descriptionIndents(NSUInteger indents) @dynamic frame, type; -- (instancetype)initWithLayoutableObject:(id)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - position:(CGPoint)position - sublayouts:(NSArray *)sublayouts +- (instancetype)initWithLayoutable:(id)layoutable + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - position:(CGPoint)position - sublayouts:(NSArray *)sublayouts ++ (instancetype)layoutWithLayoutable:(id)layoutable + size:(CGSize)size + position:(CGPoint)position + sublayouts:(nullable NSArray *)sublayouts { - return [[self alloc] initWithLayoutableObject:layoutableObject - constrainedSizeRange:sizeRange - size:size - position:position - sublayouts:sublayouts]; -} - -+ (instancetype)layoutWithLayoutableObject:(id)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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size ++ (instancetype)layoutWithLayoutable:(id)layoutable + size:(CGSize)size + sublayouts:(nullable NSArray *)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)layoutableObject - constrainedSizeRange:(ASSizeRange)sizeRange - size:(CGSize)size - sublayouts:(nullable NSArray *)sublayouts ++ (instancetype)layoutWithLayoutable:(id)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:@"<, layoutable = %@, position = %@; size = %@; constrainedSizeRange = %@>", - self, self.layoutableObject, NSStringFromCGPoint(self.position), NSStringFromCGSize(self.size), NSStringFromASSizeRange(self.constrainedSizeRange)]; + return [NSString stringWithFormat:@"<, 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 layoutable, const ASSizeRange sizeRange, const CGSize parentSize) +{ + ASDisplayNodeCAssertNotNil(layoutable, @"Not valid layoutable passed in."); + + return [layoutable layoutThatFits:sizeRange parentSize:parentSize]; +} + +ASLayout *ASCalculateRootLayout(id rootLayoutable, const ASSizeRange sizeRange) +{ + ASLayout *layout = ASCalculateLayout(rootLayoutable, sizeRange, sizeRange.max); + // Here could specific verfication happen + return layout; +} + diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.h b/AsyncDisplayKit/Layout/ASLayoutSpec.h index 72d5b2445c..371d72c374 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.h @@ -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 /** @@ -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)layoutable; + +/* + * Returns an ASWrapperLayoutSpec object initialized with the given layoutable as child + */ +- (instancetype)initWithLayoutable:(id)layoutable NS_DESIGNATED_INITIALIZER;; + +/* + * Init not available for ASWrapperLayoutSpec + */ +- (instancetype)init __unavailable; + +@end + @interface ASLayoutSpec (Debugging) /** * Used by other layout specs to create ascii art debug strings diff --git a/AsyncDisplayKit/Layout/ASLayoutSpec.mm b/AsyncDisplayKit/Layout/ASLayoutSpec.mm index 4c66b3c129..7c2371b75c 100644 --- a/AsyncDisplayKit/Layout/ASLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASLayoutSpec.mm @@ -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, std::less> 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, std::less> 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)finalLayoutable @@ -233,6 +296,37 @@ ASEnvironmentLayoutExtensibilityForwarding @end + +#pragma mark - ASWrapperLayoutSpec + +@implementation ASWrapperLayoutSpec + ++ (instancetype)wrapperWithLayoutable:(id)layoutable +{ + return [[self alloc] initWithLayoutable:layoutable]; +} + +- (instancetype)initWithLayoutable:(id)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 diff --git a/AsyncDisplayKit/Layout/ASLayoutValidation.mm b/AsyncDisplayKit/Layout/ASLayoutValidation.mm index 1aa52133a9..6f1545427f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutValidation.mm +++ b/AsyncDisplayKit/Layout/ASLayoutValidation.mm @@ -65,28 +65,14 @@ static NSString *ASLayoutValidationWrappingAssertMessage(SEL selector, id obj, C - (void)validateLayout:(ASLayout *)layout { for (ASLayout *sublayout in layout.sublayouts) { - id layoutable = layout.layoutableObject; - id sublayoutLayoutable = sublayout.layoutableObject; + id layoutable = layout.layoutable; + id 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 layoutable = layout.layoutableObject; + id layoutable = layout.layoutable; for (ASLayout *sublayout in layout.sublayouts) { - id sublayoutLayoutable = sublayout.layoutableObject; + id 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); diff --git a/AsyncDisplayKit/Layout/ASLayoutable.h b/AsyncDisplayKit/Layout/ASLayoutable.h index ad1d1f8b92..deb0e3033f 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.h +++ b/AsyncDisplayKit/Layout/ASLayoutable.h @@ -9,7 +9,6 @@ // #import -#import #import #import #import @@ -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 diff --git a/AsyncDisplayKit/Layout/ASLayoutable.mm b/AsyncDisplayKit/Layout/ASLayoutable.mm index de0f6e5cad..e3a9890999 100644 --- a/AsyncDisplayKit/Layout/ASLayoutable.mm +++ b/AsyncDisplayKit/Layout/ASLayoutable.mm @@ -16,6 +16,9 @@ #import +CGFloat const ASLayoutableParentDimensionUndefined = NAN; +CGSize const ASLayoutableParentSizeUndefined = {ASLayoutableParentDimensionUndefined, ASLayoutableParentDimensionUndefined}; + int32_t const ASLayoutableContextInvalidTransitionID = 0; int32_t const ASLayoutableContextDefaultTransitionID = ASLayoutableContextInvalidTransitionID + 1; diff --git a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h index 36031ca96f..a31405a616 100644 --- a/AsyncDisplayKit/Layout/ASLayoutablePrivate.h +++ b/AsyncDisplayKit/Layout/ASLayoutablePrivate.h @@ -8,7 +8,7 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import +#import "ASDimension.h" @class ASLayoutSpec; @protocol ASLayoutable; @@ -40,6 +40,11 @@ extern void ASLayoutableClearCurrentContext(); */ @protocol ASLayoutablePrivate +/** + * @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;\ diff --git a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm index 02f7b4d5b4..81a261bd7e 100644 --- a/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASOverlayLayoutSpec.mm @@ -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 diff --git a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm index 32e1bdea84..1d7ab611f3 100644 --- a/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRatioLayoutSpec.mm @@ -47,16 +47,18 @@ _ratio = ratio; } -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { std::vector 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 diff --git a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm b/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm index d705297cc8..8907de7209 100644 --- a/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASRelativeLayoutSpec.mm @@ -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 diff --git a/AsyncDisplayKit/Layout/ASRelativeSize.h b/AsyncDisplayKit/Layout/ASRelativeSize.h index 4f355044e9..38c0a3b926 100644 --- a/AsyncDisplayKit/Layout/ASRelativeSize.h +++ b/AsyncDisplayKit/Layout/ASRelativeSize.h @@ -8,68 +8,4 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import -#import -#import - -/** - 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 diff --git a/AsyncDisplayKit/Layout/ASRelativeSize.mm b/AsyncDisplayKit/Layout/ASRelativeSize.mm index 3fc7abd92d..e43980af71 100644 --- a/AsyncDisplayKit/Layout/ASRelativeSize.mm +++ b/AsyncDisplayKit/Layout/ASRelativeSize.mm @@ -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 diff --git a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm index 0b74a16332..0486a867cf 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStackLayoutSpec.mm @@ -118,7 +118,7 @@ _baselineRelativeArrangement = baselineRelativeArrangement; } -- (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize +- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize { std::vector> stackChildren; for (id 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 diff --git a/AsyncDisplayKit/Layout/ASStackLayoutable.h b/AsyncDisplayKit/Layout/ASStackLayoutable.h index ffd4251174..9932d52733 100644 --- a/AsyncDisplayKit/Layout/ASStackLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStackLayoutable.h @@ -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 diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h index 934506955b..cdd85907f0 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.h @@ -9,7 +9,6 @@ // #import -#import NS_ASSUME_NONNULL_BEGIN diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm index 516e79e721..e97eb65138 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm +++ b/AsyncDisplayKit/Layout/ASStaticLayoutSpec.mm @@ -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 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 diff --git a/AsyncDisplayKit/Layout/ASStaticLayoutable.h b/AsyncDisplayKit/Layout/ASStaticLayoutable.h index d1c3f74806..955a939653 100644 --- a/AsyncDisplayKit/Layout/ASStaticLayoutable.h +++ b/AsyncDisplayKit/Layout/ASStaticLayoutable.h @@ -8,8 +8,6 @@ // of patent rights can be found in the PATENTS file in the same directory. // -#import - 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 diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index 3e9bc3985b..57555a8d6d 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -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 _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 diff --git a/AsyncDisplayKit/Private/ASDisplayNodeLayout.h b/AsyncDisplayKit/Private/ASDisplayNodeLayout.h new file mode 100644 index 0000000000..64837acc25 --- /dev/null +++ b/AsyncDisplayKit/Private/ASDisplayNodeLayout.h @@ -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(); +}; diff --git a/AsyncDisplayKit/Private/ASDisplayNodeLayout.mm b/AsyncDisplayKit/Private/ASDisplayNodeLayout.mm new file mode 100644 index 0000000000..3e536bdf0e --- /dev/null +++ b/AsyncDisplayKit/Private/ASDisplayNodeLayout.mm @@ -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; +} diff --git a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm index 5a3c88b784..84393f5815 100644 --- a/AsyncDisplayKit/Private/ASEnvironmentInternal.mm +++ b/AsyncDisplayKit/Private/ASEnvironmentInternal.mm @@ -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; diff --git a/AsyncDisplayKit/Private/ASInternalHelpers.h b/AsyncDisplayKit/Private/ASInternalHelpers.h index 4d2d82e9e6..54e7a1b1d4 100644 --- a/AsyncDisplayKit/Private/ASInternalHelpers.h +++ b/AsyncDisplayKit/Private/ASInternalHelpers.h @@ -9,7 +9,6 @@ // #import -#import #import diff --git a/AsyncDisplayKit/Private/ASLayoutTransition.h b/AsyncDisplayKit/Private/ASLayoutTransition.h index 64869dc085..e6f37eab8a 100644 --- a/AsyncDisplayKit/Private/ASLayoutTransition.h +++ b/AsyncDisplayKit/Private/ASLayoutTransition.h @@ -12,9 +12,13 @@ #import "ASDimension.h" #import "_ASTransitionContext.h" +#import "ASDisplayNodeLayout.h" + +#import + +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 previousLayout; /** * Pending layout to transition to */ -@property (nonatomic, readonly, strong) ASLayout *pendingLayout; +@property (nonatomic, readonly, assign) std::shared_ptr 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)pendingLayout + previousLayout:(std::shared_ptr)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 diff --git a/AsyncDisplayKit/Private/ASLayoutTransition.mm b/AsyncDisplayKit/Private/ASLayoutTransition.mm index 52af19fad9..90357a266e 100644 --- a/AsyncDisplayKit/Private/ASLayoutTransition.mm +++ b/AsyncDisplayKit/Private/ASLayoutTransition.mm @@ -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)pendingLayout + previousLayout:(std::shared_ptr)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) { diff --git a/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm index 4dc3e194f2..57a5c6f2df 100644 --- a/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackBaselinePositionedLayout.mm @@ -15,7 +15,7 @@ static CGFloat baselineForItem(const ASStackLayoutSpecStyle &style, const ASLayout *layout) { - __weak id child = layout.layoutableObject; + __weak id 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 child = l.layoutableObject; + __weak id 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 child = l.layoutableObject; + __weak id 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}; } diff --git a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm index 0dfa11fb06..cabd487c54 100644 --- a/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm +++ b/AsyncDisplayKit/Private/ASStackUnpositionedLayout.mm @@ -23,15 +23,16 @@ static ASLayout *crossChildLayout(const id 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 child, @param style the layout style of the overall stack layout */ static void stretchChildrenAlongCrossDimension(std::vector &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::vectorlayout.size); for (auto &l : layouts) { - const id 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 */ static void layoutFlexibleChildrenAtZeroSize(std::vector &items, const ASStackLayoutSpecStyle &style, - const ASSizeRange &sizeRange) + const ASSizeRange &sizeRange, + const CGSize size) { for (ASStackUnpositionedItem &item : items) { const id child = item.child; @@ -227,7 +227,8 @@ static void layoutFlexibleChildrenAtZeroSize(std::vector &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 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 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 layoutChildrenAlongUnconstrainedStac { const CGFloat minCrossDimension = crossDimension(style.direction, sizeRange.min); const CGFloat maxCrossDimension = crossDimension(style.direction, sizeRange.max); - return AS::map(children, [&](id 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 _constrainedSize.width) { diff --git a/AsyncDisplayKit/_ASTransitionContext.m b/AsyncDisplayKit/_ASTransitionContext.m index 031810400c..750d6806c0 100644 --- a/AsyncDisplayKit/_ASTransitionContext.m +++ b/AsyncDisplayKit/_ASTransitionContext.m @@ -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 *subnodes = [NSMutableArray array]; for (ASLayout *sublayout in [self layoutForKey:key].sublayouts) { - [subnodes addObject:(ASDisplayNode *)sublayout.layoutableObject]; + [subnodes addObject:(ASDisplayNode *)sublayout.layoutable]; } return subnodes; } diff --git a/AsyncDisplayKitTests/ASBackgroundLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASBackgroundLayoutSpecSnapshotTests.mm index 54e12d98a0..7775a3a712 100644 --- a/AsyncDisplayKitTests/ASBackgroundLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASBackgroundLayoutSpecSnapshotTests.mm @@ -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 diff --git a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm index 406b25f679..62c8dc85a6 100644 --- a/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASCenterLayoutSpecSnapshotTests.mm @@ -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]; } diff --git a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m index 1d5f0a0097..95ba170b72 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeImplicitHierarchyTests.m @@ -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; diff --git a/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm b/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm index a2156291c9..9b38bf5fa4 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm +++ b/AsyncDisplayKitTests/ASDisplayNodeLayoutTests.mm @@ -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"); } diff --git a/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m b/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m index 68f3c5b4be..2c2286b00a 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeSnapshotTests.m @@ -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); } diff --git a/AsyncDisplayKitTests/ASDisplayNodeTests.m b/AsyncDisplayKitTests/ASDisplayNodeTests.m index 583ff1fbb3..dfe8d470eb 100644 --- a/AsyncDisplayKitTests/ASDisplayNodeTests.m +++ b/AsyncDisplayKitTests/ASDisplayNodeTests.m @@ -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]; diff --git a/AsyncDisplayKitTests/ASEditableTextNodeTests.m b/AsyncDisplayKitTests/ASEditableTextNodeTests.m index 83960f7331..7ac4ad5013 100644 --- a/AsyncDisplayKitTests/ASEditableTextNodeTests.m +++ b/AsyncDisplayKitTests/ASEditableTextNodeTests.m @@ -11,6 +11,7 @@ // #import +#import #import 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)); } diff --git a/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m b/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m index a6e6b857df..8788052fc6 100644 --- a/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASImageNodeSnapshotTests.m @@ -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 diff --git a/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm index 867aa76cec..f286fa8918 100644 --- a/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASInsetLayoutSpecSnapshotTests.mm @@ -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}}; diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h index fc64d11736..c632b50791 100644 --- a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h +++ b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.h @@ -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); +} diff --git a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m index fe17358653..e2d908d012 100644 --- a/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m +++ b/AsyncDisplayKitTests/ASLayoutSpecSnapshotTestsHelper.m @@ -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 diff --git a/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm index 0485605978..3e654a9417 100644 --- a/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASOverlayLayoutSpecSnapshotTests.mm @@ -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 diff --git a/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm index 9cc878b6c5..ccede39d22 100644 --- a/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASRatioLayoutSpecSnapshotTests.mm @@ -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]; diff --git a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm index fdbf3713b3..4b903dffca 100644 --- a/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASRelativeLayoutSpecSnapshotTests.mm @@ -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]; } diff --git a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm index 3b30272d45..32e5a53d41 100644 --- a/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm +++ b/AsyncDisplayKitTests/ASStackLayoutSpecSnapshotTests.mm @@ -23,26 +23,31 @@ #pragma mark - Utility methods -static NSArray *defaultSubnodes() +static NSArray *defaultSubnodes() { return defaultSubnodesWithSameSize(CGSizeZero, NO); } -static NSArray *defaultSubnodesWithSameSize(CGSize subnodeSize, BOOL flex) +static NSArray *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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. diff --git a/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m b/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m index b747dbb098..103ea75589 100644 --- a/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m +++ b/AsyncDisplayKitTests/ASStaticLayoutSpecSnapshotTests.m @@ -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]; } diff --git a/AsyncDisplayKitTests/ASTableViewTests.m b/AsyncDisplayKitTests/ASTableViewTests.m index 2c282de139..329be7e4a3 100644 --- a/AsyncDisplayKitTests/ASTableViewTests.m +++ b/AsyncDisplayKitTests/ASTableViewTests.m @@ -148,7 +148,7 @@ - (ASSizeRange)tableView:(ASTableView *)tableView constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath { - return ASSizeRangeMakeExactSize(CGSizeMake(10, 42)); + return ASSizeRangeMake(CGSizeMake(10, 42)); } @end diff --git a/AsyncDisplayKitTests/ASTextNodePerformanceTests.m b/AsyncDisplayKitTests/ASTextNodePerformanceTests.m index 6f0f1c1730..f585d50102 100644 --- a/AsyncDisplayKitTests/ASTextNodePerformanceTests.m +++ b/AsyncDisplayKitTests/ASTextNodePerformanceTests.m @@ -9,6 +9,7 @@ #import #import "ASPerformanceTestContext.h" #import +#import #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); diff --git a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m index b2c64caff8..00e6b9579c 100644 --- a/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m +++ b/AsyncDisplayKitTests/ASTextNodeSnapshotTests.m @@ -10,8 +10,8 @@ // #import "ASSnapshotTestCase.h" - #import +#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); diff --git a/AsyncDisplayKitTests/ASTextNodeTests.m b/AsyncDisplayKitTests/ASTextNodeTests.m index cb96f4fcd7..2448f0749f 100644 --- a/AsyncDisplayKitTests/ASTextNodeTests.m +++ b/AsyncDisplayKitTests/ASTextNodeTests.m @@ -12,6 +12,7 @@ #import +#import #import #import @@ -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"); } diff --git a/Base/ASBaseDefines.h b/Base/ASBaseDefines.h index 1800011d90..dd76c22040 100755 --- a/Base/ASBaseDefines.h +++ b/Base/ASBaseDefines.h @@ -151,3 +151,5 @@ #define AS_UNAVAILABLE(message) #endif #endif + +#define ASOVERLOADABLE __attribute__((overloadable)) diff --git a/examples/ASDKLayoutTransition/Sample/ViewController.m b/examples/ASDKLayoutTransition/Sample/ViewController.m index 1bda2a9307..8b7465067e 100644 --- a/examples/ASDKLayoutTransition/Sample/ViewController.m +++ b/examples/ASDKLayoutTransition/Sample/ViewController.m @@ -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); } diff --git a/examples/ASDKTube/Sample/Nodes/VideoContentCell.m b/examples/ASDKTube/Sample/Nodes/VideoContentCell.m index 34e16b5e83..3850275a95 100644 --- a/examples/ASDKTube/Sample/Nodes/VideoContentCell.m +++ b/examples/ASDKTube/Sample/Nodes/VideoContentCell.m @@ -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]; diff --git a/examples/ASDKgram/Sample/PhotoCellNode.m b/examples/ASDKgram/Sample/PhotoCellNode.m index bbb3cbd468..8d987318c8 100644 --- a/examples/ASDKgram/Sample/PhotoCellNode.m +++ b/examples/ASDKgram/Sample/PhotoCellNode.m @@ -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 diff --git a/examples/ASMapNode/Sample/MapHandlerNode.m b/examples/ASMapNode/Sample/MapHandlerNode.m index fc55eb6bc6..593f5ff231 100644 --- a/examples/ASMapNode/Sample/MapHandlerNode.m +++ b/examples/ASMapNode/Sample/MapHandlerNode.m @@ -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]; diff --git a/examples/ASViewController/Sample/DetailCellNode.m b/examples/ASViewController/Sample/DetailCellNode.m index 47016ab8b6..c8448557a0 100644 --- a/examples/ASViewController/Sample/DetailCellNode.m +++ b/examples/ASViewController/Sample/DetailCellNode.m @@ -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]]; } diff --git a/examples/ASViewController/Sample/DetailRootNode.m b/examples/ASViewController/Sample/DetailRootNode.m index 658a9b78d7..426797048d 100644 --- a/examples/ASViewController/Sample/DetailRootNode.m +++ b/examples/ASViewController/Sample/DetailRootNode.m @@ -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]]; } diff --git a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m index d406f3b170..42d3b9a08b 100644 --- a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m +++ b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASCollectionNode.m @@ -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 - diff --git a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASPagerNode.m b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASPagerNode.m index b2a4499b89..7b06ef543b 100644 --- a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASPagerNode.m +++ b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASPagerNode.m @@ -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 diff --git a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m index fc31b59ffa..25cd559dc7 100644 --- a/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m +++ b/examples/AsyncDisplayKitOverview/Sample/Node Containers/OverviewASTableNode.m @@ -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]; } diff --git a/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m b/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m index 853b2ecc97..1cfacbb5fd 100644 --- a/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m +++ b/examples/AsyncDisplayKitOverview/Sample/OverviewComponentsViewController.m @@ -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; } diff --git a/examples/AsyncDisplayKitOverview/Sample/OverviewDetailViewController.m b/examples/AsyncDisplayKitOverview/Sample/OverviewDetailViewController.m index c5bfdb146c..af2a39cb6b 100644 --- a/examples/AsyncDisplayKitOverview/Sample/OverviewDetailViewController.m +++ b/examples/AsyncDisplayKitOverview/Sample/OverviewDetailViewController.m @@ -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 diff --git a/examples/CatDealsCollectionView/Sample/ItemNode.m b/examples/CatDealsCollectionView/Sample/ItemNode.m index 8515cc8f49..4874f338d8 100644 --- a/examples/CatDealsCollectionView/Sample/ItemNode.m +++ b/examples/CatDealsCollectionView/Sample/ItemNode.m @@ -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; } diff --git a/examples/CatDealsCollectionView/Sample/LoadingNode.m b/examples/CatDealsCollectionView/Sample/LoadingNode.m index 43a7693700..dafc883d1f 100644 --- a/examples/CatDealsCollectionView/Sample/LoadingNode.m +++ b/examples/CatDealsCollectionView/Sample/LoadingNode.m @@ -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]; diff --git a/examples/CustomCollectionView/Sample/ImageCellNode.m b/examples/CustomCollectionView/Sample/ImageCellNode.m index 5eb7163c32..73b1d373a4 100644 --- a/examples/CustomCollectionView/Sample/ImageCellNode.m +++ b/examples/CustomCollectionView/Sample/ImageCellNode.m @@ -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 diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm index a7b5989591..b5ba08889b 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm +++ b/examples/HorizontalWithinVerticalScrolling/Sample/HorizontalScrollCellNode.mm @@ -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; } diff --git a/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m index 0f3afbae0c..fbc1cd7ee7 100644 --- a/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m +++ b/examples/HorizontalWithinVerticalScrolling/Sample/ViewController.m @@ -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 diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index e97ef637ff..f75fc101f8 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -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. diff --git a/examples/PagerNode/Sample/PageNode.m b/examples/PagerNode/Sample/PageNode.m index 4db8e4f966..264809ce17 100644 --- a/examples/PagerNode/Sample/PageNode.m +++ b/examples/PagerNode/Sample/PageNode.m @@ -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 diff --git a/examples/SocialAppLayout/Sample/CommentsNode.m b/examples/SocialAppLayout/Sample/CommentsNode.m index de1a009945..33ad608f45 100644 --- a/examples/SocialAppLayout/Sample/CommentsNode.m +++ b/examples/SocialAppLayout/Sample/CommentsNode.m @@ -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 diff --git a/examples/SocialAppLayout/Sample/LikesNode.m b/examples/SocialAppLayout/Sample/LikesNode.m index c0d9d78ee7..041775e562 100644 --- a/examples/SocialAppLayout/Sample/LikesNode.m +++ b/examples/SocialAppLayout/Sample/LikesNode.m @@ -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 diff --git a/examples/SocialAppLayout/Sample/PostNode.m b/examples/SocialAppLayout/Sample/PostNode.m index 9187999602..6b45f2f2b1 100644 --- a/examples/SocialAppLayout/Sample/PostNode.m +++ b/examples/SocialAppLayout/Sample/PostNode.m @@ -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]; } diff --git a/examples/SocialAppLayout/Sample/ViewController.m b/examples/SocialAppLayout/Sample/ViewController.m index 9a2692f5d0..492861c01b 100644 --- a/examples/SocialAppLayout/Sample/ViewController.m +++ b/examples/SocialAppLayout/Sample/ViewController.m @@ -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; diff --git a/examples/Swift/Sample/AppDelegate.swift b/examples/Swift/Sample/AppDelegate.swift index 7bcf44e432..2aaa33f9c6 100644 --- a/examples/Swift/Sample/AppDelegate.swift +++ b/examples/Swift/Sample/AppDelegate.swift @@ -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 diff --git a/examples/Swift/Sample/TailLoadingCellNode.swift b/examples/Swift/Sample/TailLoadingCellNode.swift index 6defdbd293..101b30335a 100644 --- a/examples/Swift/Sample/TailLoadingCellNode.swift +++ b/examples/Swift/Sample/TailLoadingCellNode.swift @@ -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() { diff --git a/examples/Swift/Sample/ViewController.swift b/examples/Swift/Sample/ViewController.swift index a23486bd7d..0982081c0a 100644 --- a/examples/Swift/Sample/ViewController.swift +++ b/examples/Swift/Sample/ViewController.swift @@ -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 diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm b/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm index d86f831dd5..eaa01e7d6d 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm +++ b/examples/VerticalWithinHorizontalScrolling/Sample/GradientTableNode.mm @@ -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; } diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m index 321d04d4b2..74dd517084 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m +++ b/examples/VerticalWithinHorizontalScrolling/Sample/RandomCoreGraphicsNode.m @@ -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 diff --git a/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m b/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m index c474bbbbae..e7311a3387 100644 --- a/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m +++ b/examples/VerticalWithinHorizontalScrolling/Sample/ViewController.m @@ -77,7 +77,7 @@ CGSize boundsSize = pagerNode.bounds.size; CGSize gradientRowSize = CGSizeMake(boundsSize.width, 100); GradientTableNode *node = [[GradientTableNode alloc] initWithElementSize:gradientRowSize]; - node.preferredFrameSize = boundsSize; + [node setSizeWithCGSize:boundsSize]; node.pageNumber = index; return node; } diff --git a/examples/Videos/Sample/ViewController.m b/examples/Videos/Sample/ViewController.m index 1b390386a2..faa4b8b8d9 100644 --- a/examples/Videos/Sample/ViewController.m +++ b/examples/Videos/Sample/ViewController.m @@ -49,18 +49,22 @@ ASVideoNode *hlsVideoNode = self.hlsVideoNode; [_rootNode addSubnode:hlsVideoNode]; + CGSize mainScreenBoundsSize = [UIScreen mainScreen].bounds.size; + _rootNode.layoutSpecBlock = ^ASLayoutSpec *(ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { + + // Layout all nodes absolute in a static layout spec + [guitarVideoNode setSizeWithCGSize:CGSizeMake(mainScreenBoundsSize.width, mainScreenBoundsSize.height / 3.0)]; guitarVideoNode.layoutPosition = CGPointMake(0, 0); - guitarVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/3); - nicCageVideoNode.layoutPosition = CGPointMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3); - nicCageVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3); + [nicCageVideoNode setSizeWithCGSize:CGSizeMake(mainScreenBoundsSize.width/2, mainScreenBoundsSize.height / 3.0)]; + nicCageVideoNode.layoutPosition = CGPointMake(mainScreenBoundsSize.width / 2.0, mainScreenBoundsSize.height / 3.0); - simonVideoNode.layoutPosition = CGPointMake(0, [UIScreen mainScreen].bounds.size.height - ([UIScreen mainScreen].bounds.size.height/3)); - simonVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3); + [simonVideoNode setSizeWithCGSize:CGSizeMake(mainScreenBoundsSize.width/2, mainScreenBoundsSize.height / 3.0)]; + simonVideoNode.layoutPosition = CGPointMake(0.0, mainScreenBoundsSize.height - (mainScreenBoundsSize.height / 3.0)); - hlsVideoNode.layoutPosition = CGPointMake(0, [UIScreen mainScreen].bounds.size.height/3); - hlsVideoNode.preferredFrameSize = CGSizeMake([UIScreen mainScreen].bounds.size.width/2, [UIScreen mainScreen].bounds.size.height/3); + [hlsVideoNode setSizeWithCGSize:CGSizeMake(mainScreenBoundsSize.width / 2.0, mainScreenBoundsSize.height / 3.0)]; + hlsVideoNode.layoutPosition = CGPointMake(0.0, mainScreenBoundsSize.height / 3.0); return [ASStaticLayoutSpec staticLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]]; }; diff --git a/examples_extra/ASTraitCollection/Sample/KittenNode.m b/examples_extra/ASTraitCollection/Sample/KittenNode.m index d6ac688bfe..0a9dacbde6 100644 --- a/examples_extra/ASTraitCollection/Sample/KittenNode.m +++ b/examples_extra/ASTraitCollection/Sample/KittenNode.m @@ -77,7 +77,7 @@ static const CGFloat kInnerPadding = 10.0f; // kitten image, with a solid background colour serving as placeholder _imageNode = [[ASNetworkImageNode alloc] init]; _imageNode.backgroundColor = ASDisplayNodeDefaultPlaceholderColor(); - _imageNode.preferredFrameSize = _kittenSize; + _imageNode.size = ASRelativeSizeRangeMakeWithExactCGSize(_kittenSize); [_imageNode addTarget:self action:@selector(imageTapped:) forControlEvents:ASControlNodeEventTouchUpInside]; CGFloat scale = [UIScreen mainScreen].scale; diff --git a/examples_extra/BackgroundPropertySetting/Sample/DemoCellNode.swift b/examples_extra/BackgroundPropertySetting/Sample/DemoCellNode.swift index 3fac84dfcc..1f4ae0e344 100644 --- a/examples_extra/BackgroundPropertySetting/Sample/DemoCellNode.swift +++ b/examples_extra/BackgroundPropertySetting/Sample/DemoCellNode.swift @@ -32,10 +32,10 @@ final class DemoCellNode: ASCellNode { override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec { let specA = ASRatioLayoutSpec(ratio: 1, child: childA) - specA.flexBasis = ASRelativeDimensionMakeWithPoints(1) + specA.flexBasis = ASDimensionMakeWithPoints(1) specA.flexGrow = true let specB = ASRatioLayoutSpec(ratio: 1, child: childB) - specB.flexBasis = ASRelativeDimensionMakeWithPoints(1) + specB.flexBasis = ASDimensionMakeWithPoints(1) specB.flexGrow = true let children = state.isReverse ? [ specB, specA ] : [ specA, specB ] let direction: ASStackLayoutDirection = state.isVertical ? .Vertical : .Horizontal diff --git a/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m b/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m index 328cbba515..3ab470844a 100644 --- a/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m +++ b/examples_extra/SynchronousConcurrency/Sample/AsyncTableViewController.m @@ -30,24 +30,12 @@ @implementation AsyncTableViewController -#pragma mark - -#pragma mark UIViewController. +#pragma mark - UIViewController. - (instancetype)init { if (!(self = [super init])) return nil; - - _tableView = [[ASTableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; - _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; - _tableView.asyncDataSource = self; - _tableView.asyncDelegate = self; - - ASRangeTuningParameters tuningParameters; - tuningParameters.leadingBufferScreenfuls = 0.5; - tuningParameters.trailingBufferScreenfuls = 1.0; - [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypePreload]; - [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; self.tabBarItem = [[UITabBarItem alloc] initWithTabBarSystemItem:UITabBarSystemItemFeatured tag:0]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRedo @@ -66,22 +54,30 @@ { [super viewDidLoad]; + _tableView = [[ASTableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; + _tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + _tableView.separatorStyle = UITableViewCellSeparatorStyleNone; + _tableView.asyncDataSource = self; + _tableView.asyncDelegate = self; + + ASRangeTuningParameters tuningParameters; + tuningParameters.leadingBufferScreenfuls = 0.5; + tuningParameters.trailingBufferScreenfuls = 1.0; + [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypePreload]; + [_tableView setTuningParameters:tuningParameters forRangeType:ASLayoutRangeTypeRender]; + [self.view addSubview:_tableView]; } -- (void)viewWillLayoutSubviews -{ - _tableView.frame = self.view.bounds; -} +#pragma mark - ASTableView. -#pragma mark - -#pragma mark ASTableView. - -- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath +- (ASCellNodeBlock)tableView:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath { - RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; - elementNode.preferredFrameSize = CGSizeMake(320, 100); - return elementNode; + return ^{ + RandomCoreGraphicsNode *elementNode = [[RandomCoreGraphicsNode alloc] init]; + elementNode.size = ASRelativeSizeRangeMakeWithExactCGSize(CGSizeMake(320, 100)); + return elementNode; + }; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section diff --git a/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m b/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m index f6a94959a7..d2afbd6e17 100644 --- a/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m +++ b/examples_extra/SynchronousConcurrency/Sample/RandomCoreGraphicsNode.m @@ -92,6 +92,8 @@ - (void)layout { + [super layout]; + CGSize boundsSize = self.bounds.size; CGSize textSize = _textNode.calculatedSize; CGRect textRect = CGRectMake(roundf((boundsSize.width - textSize.width) / 2.0), diff --git a/examples_extra/SynchronousKittens/Sample/KittenNode.mm b/examples_extra/SynchronousKittens/Sample/KittenNode.mm index bf151fd837..8c8ba288bb 100644 --- a/examples_extra/SynchronousKittens/Sample/KittenNode.mm +++ b/examples_extra/SynchronousKittens/Sample/KittenNode.mm @@ -140,7 +140,9 @@ static const CGFloat kInnerPadding = 10.0f; #if UseAutomaticLayout - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - _imageNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); + CGSize imageSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) + : CGSizeMake(kImageSize, kImageSize); + _imageNode.size = ASRelativeSizeRangeMakeWithExactCGSize(imageSize); _textNode.flexShrink = YES; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; diff --git a/examples_extra/VideoTableView/Sample/NicCageNode.mm b/examples_extra/VideoTableView/Sample/NicCageNode.mm index cb6802229f..6e3d8ea471 100644 --- a/examples_extra/VideoTableView/Sample/NicCageNode.mm +++ b/examples_extra/VideoTableView/Sample/NicCageNode.mm @@ -165,7 +165,9 @@ static const CGFloat kInnerPadding = 10.0f; #if UseAutomaticLayout - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize { - _videoNode.preferredFrameSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) : CGSizeMake(kImageSize, kImageSize); + CGSize videoNodeSize = _isImageEnlarged ? CGSizeMake(2.0 * kImageSize, 2.0 * kImageSize) + : CGSizeMake(kImageSize, kImageSize); + _videoNode.size = ASRelativeSizeRangeMakeWithExactCGSize(videoNodeSize); _textNode.flexShrink = YES; ASStackLayoutSpec *stackSpec = [[ASStackLayoutSpec alloc] init]; diff --git a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m index 761d164c28..05ac244597 100644 --- a/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m +++ b/smoke-tests/Life Without CocoaPods/Life Without CocoaPods/ViewController.m @@ -22,7 +22,7 @@ { self.textNode = [[ASTextNode alloc] init]; self.textNode.attributedText = [[NSAttributedString alloc] initWithString:@"Testing, testing." attributes:@{ NSForegroundColorAttributeName: [UIColor redColor] }]; - [self.textNode measure:self.view.bounds.size]; + [self.textNode layoutThatFits:ASSizeRangeMake(CGSizeZero, self.view.bounds.size)]; self.textNode.frame = (CGRect){ .origin = CGPointZero, .size = self.textNode.calculatedSize }; [self.view addSubnode:self.textNode]; }