diff --git a/AsyncDisplayKit/ASCellNode.mm b/AsyncDisplayKit/ASCellNode.mm index 77270c3736..fee0e53935 100644 --- a/AsyncDisplayKit/ASCellNode.mm +++ b/AsyncDisplayKit/ASCellNode.mm @@ -29,7 +29,7 @@ #pragma mark - #pragma mark ASCellNode -@interface ASCellNode () +@interface ASCellNode () { ASDisplayNodeViewControllerBlock _viewControllerBlock; ASDisplayNodeDidLoadBlock _viewControllerDidLoadBlock; @@ -58,9 +58,6 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init // Use UITableViewCell defaults _selectionStyle = UITableViewCellSelectionStyleDefault; self.clipsToBounds = YES; - - // ASCellNode acts as sizing container for the node - self.sizingDelegate = self; return self; } @@ -121,19 +118,16 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init _viewControllerNode.frame = self.bounds; } -- (void)displayNodeDidInvalidateSize:(ASDisplayNode *)displayNode +- (void)didInvalidateSize { - ASDN::MutexLocker l(__instanceLock__); + // TODO: coalesc: Ask the UITableView for the proper constrained size it can layout + CGSize oldSize = self.calculatedSize; + CGSize newSize = [self sizeThatFits:CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX)]; - CGSize oldSize = self.calculatedSize; - ASLayout *layout = [self layoutThatFits:self.constrainedSizeForCalculatedLayout]; - - // TODO: Needs proper adjustment - CGRect f = self.frame; - f.size = layout.size; - self.frame = f; - - [self didRelayoutFromOldSize:oldSize toNewSize:layout.size]; + if (CGSizeEqualToSize(oldSize, newSize) == NO) { + self.frame = {self.frame.origin, newSize}; + [self didRelayoutFromOldSize:oldSize toNewSize:newSize]; + } } - (void)transitionLayoutWithAnimation:(BOOL)animated diff --git a/AsyncDisplayKit/ASCollectionView.mm b/AsyncDisplayKit/ASCollectionView.mm index cc870a9f79..8f4f3aa37e 100644 --- a/AsyncDisplayKit/ASCollectionView.mm +++ b/AsyncDisplayKit/ASCollectionView.mm @@ -765,7 +765,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell"; - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - return [[self nodeForItemAtIndexPath:indexPath] calculatedSize]; + ASDisplayNode *node = [self nodeForItemAtIndexPath:indexPath]; + [node layoutIfNeeded]; + return node.calculatedSize; } - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index f9ebca2af7..06078abaef 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -267,8 +267,9 @@ extern NSInteger const ASDefaultDrawingPriority; /** @name Managing dimensions */ -@property (nonatomic, readwrite, weak, nullable) id sizingDelegate; +//@property (nonatomic, readwrite, weak, nullable) id sizingDelegate; - (void)invalidateSize; +- (void)didInvalidateSize; - (void)sizeToFit; - (CGSize)sizeThatFits:(CGSize)size; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index fb3e4cb5eb..be1db2c911 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -709,14 +709,18 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) __instanceLock__.lock(); + // Mark the node for layout in the next layout pass [self invalidateCalculatedLayout]; // This is the root node. Let the delegate know that the size changed // If someone calls `invalidateBlaBla TBD` we have to inform the sizing delegate of the root node to be able // to let them now that a size change happened and it needs to calculate a new layout / size for this node hierarchy - if ([self.sizingDelegate respondsToSelector:@selector(displayNodeDidInvalidateSize:)]) { - [self.sizingDelegate displayNodeDidInvalidateSize:self]; - } +// if ([self.sizingDelegate respondsToSelector:@selector(displayNodeDidInvalidateSize:)]) { +// [self.sizingDelegate displayNodeDidInvalidateSize:self]; +// } + + // Hook for subclasses to get size invalidation changes + [self didInvalidateSize]; if (_supernode) { ASDisplayNode *supernode = _supernode; @@ -727,9 +731,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return; } + // We are now at the root node trigger a layout pass + [self setNeedsLayout]; + __instanceLock__.unlock(); } +- (void)didInvalidateSize +{ + +} + - (void)sizeToFit { ASDisplayNodeAssertThreadAffinity(self); @@ -739,11 +751,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self setNeedsLayout]; CGSize maxSize = _supernode ? _supernode.bounds.size : CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX); - CGSize size = [self sizeThatFits:maxSize]; + CGSize newSize = [self sizeThatFits:maxSize]; CGRect oldBounds = self.bounds; CGSize oldSize = oldBounds.size; - CGSize newSize = _calculatedDisplayNodeLayout->layout.size; if (! CGSizeEqualToSize(oldSize, newSize)) { self.bounds = (CGRect){ oldBounds.origin, newSize }; @@ -777,74 +788,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { ASDN::MutexLocker l(__instanceLock__); - if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:parentSize] == NO) { + if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize)) { ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _layout should not be nil! %@", self); return _calculatedDisplayNodeLayout->layout ? : [ASLayout layoutWithLayoutElement:self size:{0, 0}]; } - - [self cancelLayoutTransition]; - - BOOL didCreateNewContext = NO; - BOOL didOverrideExistingContext = NO; - BOOL shouldVisualizeLayout = ASHierarchyStateIncludesVisualizeLayout(_hierarchyState); - ASLayoutElementContext context; - if (ASLayoutElementContextIsNull(ASLayoutElementGetCurrentContext())) { - context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID, shouldVisualizeLayout); - ASLayoutElementSetCurrentContext(context); - didCreateNewContext = YES; - } else { - context = ASLayoutElementGetCurrentContext(); - if (context.needsVisualizeNode != shouldVisualizeLayout) { - context.needsVisualizeNode = shouldVisualizeLayout; - ASLayoutElementSetCurrentContext(context); - didOverrideExistingContext = YES; - } - } - - // Prepare for layout transition - auto previousLayout = _calculatedDisplayNodeLayout; - auto pendingLayout = std::make_shared( - [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize], - constrainedSize, - parentSize - ); - - if (didCreateNewContext) { - ASLayoutElementClearCurrentContext(); - } else if (didOverrideExistingContext) { - context.needsVisualizeNode = !context.needsVisualizeNode; - ASLayoutElementSetCurrentContext(context); - } - - _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self - 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(pendingLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] newLayout should not be nil! %@", self); - return pendingLayout->layout; -} - -- (BOOL)shouldCalculateLayoutWithConstrainedSize:(ASSizeRange)constrainedSize parentSize:(CGSize)parentSize -{ - ASDN::MutexLocker l(__instanceLock__); - - // Don't remeasure if in layout pending state and a new transition already started - if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { - ASLayoutElementContext context = ASLayoutElementGetCurrentContext(); - if (ASLayoutElementContextIsNull(context) || _pendingTransitionID != context.transitionID) { - return NO; - } - } - - // Check if display node layout is still valid - return _calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, parentSize) == NO; + + return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize]; } - (ASLayoutElementType)layoutElementType @@ -883,7 +832,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) return; } - [self invalidateCalculatedLayout]; + [self setNeedsLayout]; [self transitionLayoutWithSizeRange:_calculatedDisplayNodeLayout->constrainedSize animated:animated shouldMeasureAsync:shouldMeasureAsync @@ -898,7 +847,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) { // Passed constrainedSize is the the same as the node's current constrained size it's a noop ASDisplayNodeAssertMainThread(); - if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:constrainedSize.max] == NO) { + if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, constrainedSize.max)) { return; } @@ -1449,13 +1398,18 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self displayImmediately]; } +- (void)invalidateCalculatedLayout +{ + // This will cause the next layout pass to compute a new layout instead of returning + // the cached layout in case the constrained or parent size did not change + _calculatedDisplayNodeLayout->invalidate(); +} + - (void)__setNeedsLayout { ASDN::MutexLocker l(__instanceLock__); - // 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(); + [self invalidateCalculatedLayout]; } /** @@ -1482,7 +1436,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [supernode __layoutIfNeeded]; } else { // Layout all subviews starting from the first node that needs layout - [self __layout]; + [self __layoutSublayers]; } } @@ -1498,13 +1452,11 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // These private methods ensure that subclasses are not required to call super in order for _renderingSubnodes to be properly managed. -- (void)__layout +- (void)__layoutSublayers { ASDisplayNodeAssertMainThread(); ASDN::MutexLocker l(__instanceLock__); - CGRect bounds = self.bounds; - - [self measureNodeWithBoundsIfNecessary:bounds]; + CGRect bounds = _threadSafeBounds; if (CGRectEqualToRect(bounds, CGRectZero)) { // Performing layout on a zero-bounds view often results in frame calculations @@ -1512,6 +1464,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // measureWithSizeRange: on subnodes to assert. return; } + + [self measureNodeWithBoundsIfNecessary:bounds]; // Handle placeholder layer creation in case the size of the node changed after the initial placeholder layer // was created @@ -1524,18 +1478,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) [self layoutDidFinish]; } +/// Needs to be called with lock held - (void)measureNodeWithBoundsIfNecessary:(CGRect)bounds { - BOOL supportsRangeManagedInterfaceState = NO; - BOOL hasDirtyLayout = NO; - CGSize calculatedLayoutSize = CGSizeZero; - { - ASDN::MutexLocker l(__instanceLock__); - supportsRangeManagedInterfaceState = [self supportsRangeManagedInterfaceState]; - hasDirtyLayout = _calculatedDisplayNodeLayout->isDirty(); - calculatedLayoutSize = _calculatedDisplayNodeLayout->layout.size; - } - // Check if it's a subnode in a layout transition. In this case no measurement is needed as it's part of // the layout transition if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { @@ -1549,8 +1494,60 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) // for the node using a size range equal to whatever bounds were provided to the node 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 if (hasDirtyLayout || CGSizeEqualToSize(calculatedLayoutSize, bounds.size) == NO) { - [self layoutThatFits:ASSizeRangeMake(bounds.size)]; + return; + } + + if (_calculatedDisplayNodeLayout->isDirty() || + CGSizeEqualToSize(_calculatedDisplayNodeLayout->layout.size, bounds.size) == NO) + { + [self cancelLayoutTransition]; + + BOOL didCreateNewContext = NO; + BOOL didOverrideExistingContext = NO; + BOOL shouldVisualizeLayout = ASHierarchyStateIncludesVisualizeLayout(_hierarchyState); + ASLayoutElementContext context; + if (ASLayoutElementContextIsNull(ASLayoutElementGetCurrentContext())) { + context = ASLayoutElementContextMake(ASLayoutElementContextDefaultTransitionID, shouldVisualizeLayout); + ASLayoutElementSetCurrentContext(context); + didCreateNewContext = YES; + } else { + context = ASLayoutElementGetCurrentContext(); + if (context.needsVisualizeNode != shouldVisualizeLayout) { + context.needsVisualizeNode = shouldVisualizeLayout; + ASLayoutElementSetCurrentContext(context); + didOverrideExistingContext = YES; + } + } + + // Prepare for layout transition + CGSize parentSize = bounds.size; + ASSizeRange constrainedSize = ASSizeRangeMake(parentSize); + auto previousLayout = _calculatedDisplayNodeLayout; + auto pendingLayout = std::make_shared( + [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize], + constrainedSize, + parentSize + ); + + if (didCreateNewContext) { + ASLayoutElementClearCurrentContext(); + } else if (didOverrideExistingContext) { + context.needsVisualizeNode = !context.needsVisualizeNode; + ASLayoutElementSetCurrentContext(context); + } + + ASDisplayNodeAssertNotNil(pendingLayout->layout, @"pendintLayout->layout should not be nil! %@", self); + + _pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self + 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]; + } } } @@ -2680,15 +2677,6 @@ void recursivelyTriggerDisplayForLayer(CALayer *layer, BOOL shouldBlock) return nil; } -- (void)invalidateCalculatedLayout -{ - ASDN::MutexLocker l(__instanceLock__); - - // 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 { ASDN::MutexLocker l(__instanceLock__); @@ -3730,7 +3718,7 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode"; { // Deprecated preferredFrameSize just calls through to set width and height self.style.preferredSize = preferredFrameSize; - [self invalidateCalculatedLayout]; + [self setNeedsLayout]; } - (CGSize)preferredFrameSize diff --git a/AsyncDisplayKit/ASImageNode.mm b/AsyncDisplayKit/ASImageNode.mm index bdc2180db1..2678a7e8a9 100644 --- a/AsyncDisplayKit/ASImageNode.mm +++ b/AsyncDisplayKit/ASImageNode.mm @@ -204,7 +204,7 @@ struct ASImageNodeDrawParameters { if (!ASObjectIsEqual(_image, image)) { _image = image; - [self invalidateCalculatedLayout]; + [self setNeedsLayout]; if (image) { [self setNeedsDisplay]; diff --git a/AsyncDisplayKit/ASTableView.mm b/AsyncDisplayKit/ASTableView.mm index ec1d8a3019..7495a3952e 100644 --- a/AsyncDisplayKit/ASTableView.mm +++ b/AsyncDisplayKit/ASTableView.mm @@ -751,6 +751,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; + [node layoutIfNeeded]; return node.calculatedSize.height; } @@ -1509,8 +1510,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; #pragma mark - _ASTableViewCellDelegate -#pragma mark - _ASTableViewCellDelegate - - (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell { ASCellNode *node = tableViewCell.node; @@ -1525,7 +1524,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell"; // Normally the content view width equals to the constrained size width (which equals to the table view width). // If there is a mismatch between these values, for example after the table view entered or left editing mode, // content view width is preferred and used to re-measure the cell node. - if (contentViewWidth != constrainedSize.max.width) { + if (CGSizeEqualToSize(node.calculatedSize, CGSizeZero) == NO && contentViewWidth != constrainedSize.max.width) { constrainedSize.min.width = contentViewWidth; constrainedSize.max.width = contentViewWidth; diff --git a/AsyncDisplayKit/ASTextNode.mm b/AsyncDisplayKit/ASTextNode.mm index f4cacda904..897088149e 100644 --- a/AsyncDisplayKit/ASTextNode.mm +++ b/AsyncDisplayKit/ASTextNode.mm @@ -330,8 +330,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(textContainerInset, _textContainerInset); if (needsUpdate) { _textContainerInset = textContainerInset; - [self invalidateCalculatedLayout]; - [self setNeedsLayout]; + [self invalidateSize]; } } @@ -487,7 +486,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; } // Tell the display node superclasses that the cached layout is incorrect now - [self invalidateCalculatedLayout]; + [self invalidateSize]; // Force display to create renderer with new size and redisplay with new string [self setNeedsDisplay]; @@ -510,7 +509,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ]; _exclusionPaths = [exclusionPaths copy]; [self _invalidateRenderer]; - [self invalidateCalculatedLayout]; + [self invalidateSize]; [self setNeedsDisplay]; } diff --git a/AsyncDisplayKit/Details/_ASDisplayLayer.mm b/AsyncDisplayKit/Details/_ASDisplayLayer.mm index dd7f70ee8f..1a49396825 100644 --- a/AsyncDisplayKit/Details/_ASDisplayLayer.mm +++ b/AsyncDisplayKit/Details/_ASDisplayLayer.mm @@ -135,7 +135,7 @@ ASDisplayNodeAssertMainThread(); [super layoutSublayers]; - [self.asyncdisplaykit_node __layout]; + [self.asyncdisplaykit_node __layoutSublayers]; } - (void)setNeedsDisplay diff --git a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm index 484b6b76a9..4ee4ad0d3c 100644 --- a/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm +++ b/AsyncDisplayKit/Private/ASDisplayNode+AsyncDisplay.mm @@ -52,7 +52,7 @@ // if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers if (rasterizingFromAscendent) { - [self __layout]; + [self __layoutSublayers]; } // Capture these outside the display block so they are retained. diff --git a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h index c26eb1c014..dc4f0eed81 100644 --- a/AsyncDisplayKit/Private/ASDisplayNodeInternal.h +++ b/AsyncDisplayKit/Private/ASDisplayNodeInternal.h @@ -212,7 +212,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo */ - (void)__setNeedsDisplay; -- (void)__layout; +- (void)__layoutSublayers; - (void)__setSupernode:(ASDisplayNode *)supernode; /** diff --git a/examples/ASViewController/Sample/DetailViewController.m b/examples/ASViewController/Sample/DetailViewController.m index 02bdceb2f9..a838ba645d 100644 --- a/examples/ASViewController/Sample/DetailViewController.m +++ b/examples/ASViewController/Sample/DetailViewController.m @@ -21,7 +21,7 @@ #import "DetailRootNode.h" #import "SampleSizingNode.h" -@interface DetailViewController () +@interface DetailViewController ()// @property (strong, nonatomic) SampleSizingNode *sizingNode; @property (strong, nonatomic) ASNetworkImageNode *imageNode; @@ -40,7 +40,7 @@ // Set the sizing delegate of the root node to the container _sizingNode = [SampleSizingNode new]; _sizingNode.autoresizingMask = UIViewAutoresizingNone; - _sizingNode.sizingDelegate = self; + //_sizingNode.sizingDelegate = self; _imageNode = [ASNetworkImageNode new]; _imageNode.needsDisplayOnBoundsChange = YES; @@ -53,7 +53,7 @@ [_buttonNode setTitle:@"Some Title" withFont:nil withColor:nil forState:ASControlStateNormal]; [_buttonNode setTitle:@"Some Bla" withFont:nil withColor:[UIColor orangeColor] forState:ASControlStateHighlighted]; [_buttonNode addTarget:self action:@selector(buttonAction:) forControlEvents:ASControlNodeEventTouchUpInside]; - _buttonNode.sizingDelegate = self; + //_buttonNode.sizingDelegate = self; return self; } @@ -74,7 +74,7 @@ // Initial size of sizing node //self.sizingNode.frame = CGRectMake(100, 100, 50, 50); - [self displayNodeDidInvalidateSize:self.buttonNode]; + //[self displayNodeDidInvalidateSize:self.buttonNode]; // Initial size for image node // self.imageNode.frame = CGRectMake(50, 70, 100, 100); @@ -94,6 +94,7 @@ { [super viewDidLayoutSubviews]; + // Updat the sizing for the button node [self updateButtonNodeLayout]; // Update the sizing node layout @@ -116,23 +117,23 @@ // The sizing delegate will get callbacks if the size did invalidate of the display node. It's the job of the delegate // to get the new size from the display node and update the frame based on the returned size -- (void)displayNodeDidInvalidateSize:(ASDisplayNode *)displayNode -{ - if (displayNode == self.buttonNode) { - [self updateButtonNodeLayout]; - return; - } - - [self updateNodeLayout]; - - /*dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - [self updateNodeLayoutRandom]; - });*/ - - /*[NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) { - [self updateNodeLayoutRandom]; - }];*/ -} +//- (void)displayNodeDidInvalidateSize:(ASDisplayNode *)displayNode +//{ +// if (displayNode == self.buttonNode) { +// [self updateButtonNodeLayout]; +// return; +// } +// +// [self updateNodeLayout]; +// +// /*dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ +// [self updateNodeLayoutRandom]; +// });*/ +// +// /*[NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) { +// [self updateNodeLayoutRandom]; +// }];*/ +//} - (void)updateNodeLayout { @@ -140,29 +141,34 @@ //return; // Use the bounds of the view and get the fitting size // This does not have any side effects, but can be called on the main thread without any problems - CGSize size = [self.sizingNode sizeThatFits:CGSizeMake(CGFLOAT_MAX, 100.0)]; + CGSize size = [self.sizingNode sizeThatFits:CGSizeMake(INFINITY, 100.0)]; //size.width -= 10; //[self.sizingNode setNeedsLayout]; - self.sizingNode.frame = CGRectMake((self.view.bounds.size.width - size.width) / 2.0, - (self.view.bounds.size.height - size.height) / 2.0, - size.width, size.height); + self.sizingNode.frame = CGRectMake((CGRectGetWidth(self.view.bounds) - size.width) / 2.0, + (CGRectGetHeight(self.view.bounds) - size.height) / 2.0, + size.width, + size.height); //dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + // Decrease the frame a bit self.sizingNode.frame = CGRectInset(self.sizingNode.frame, 10, 10); //}); } - (void)updateNodeLayoutRandom { + CGRect bounds = self.view.bounds; + // Pick a randome width and height and set the frame of the node CGSize size = CGSizeZero; - size.width = arc4random_uniform(self.view.bounds.size.width); - size.height = arc4random_uniform(self.view.bounds.size.height); + size.width = arc4random_uniform(CGRectGetWidth(bounds)); + size.height = arc4random_uniform(CGRectGetHeight(bounds)); //[self.sizingNode setNeedsLayout]; - self.sizingNode.frame = CGRectMake((self.view.bounds.size.width - size.width) / 2.0, - (self.view.bounds.size.height - size.height) / 2.0, - size.width, size.height); + self.sizingNode.frame = CGRectMake((CGRectGetWidth(bounds) - size.width) / 2.0, + (CGRectGetHeight(bounds) - size.height) / 2.0, + size.width, + size.height); } diff --git a/examples/ASViewController/Sample/SampleSizingNode.m b/examples/ASViewController/Sample/SampleSizingNode.m index a1767b12a9..674b545b09 100644 --- a/examples/ASViewController/Sample/SampleSizingNode.m +++ b/examples/ASViewController/Sample/SampleSizingNode.m @@ -8,10 +8,27 @@ #import "SampleSizingNode.h" +@interface SampleSizingNodeSubnode : ASDisplayNode +@property (strong, nonatomic) ASTextNode *textNode; +@end + +@implementation SampleSizingNodeSubnode + +- (void)layout +{ + [super layout]; + + // Manual layout after the normal layout engine did it's job + // Calculated size can be used after the layout spec pass happened + //self.textNode.frame = CGRectMake(self.textNode.frame.origin.x, self.textNode.frame.origin.y, self.textNode.calculatedSize.width, 20); +} + +@end + @interface SampleSizingNode () -@property (nonatomic, strong) ASDisplayNode *subnode; @property (nonatomic, assign) NSInteger state; +@property (nonatomic, strong) SampleSizingNodeSubnode *subnode; @property (nonatomic, strong) ASTextNode *textNode; @property (nonatomic, strong) ASNetworkImageNode *imageNode; @end @@ -35,13 +52,15 @@ _imageNode.backgroundColor = [UIColor brownColor]; _imageNode.needsDisplayOnBoundsChange = YES; _imageNode.style.height = ASDimensionMakeWithFraction(1.0); - _imageNode.style.width = ASDimensionMake(30.0); + _imageNode.style.width = ASDimensionMake(50.0); - _subnode = [ASDisplayNode new]; + _subnode = [SampleSizingNodeSubnode new]; + _subnode.textNode = _textNode; _subnode.backgroundColor = [UIColor redColor]; _subnode.automaticallyManagesSubnodes = YES; + // Layout description via layoutSpecBlock __weak __typeof(self) weakSelf = self; _subnode.layoutSpecBlock = ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { @@ -113,7 +132,6 @@ [self invalidateSize]; } - #pragma mark - ASDisplayNode - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize @@ -129,5 +147,13 @@ child:self.subnode]; } +- (void)layout +{ + [super layout]; + + // Layout after the official layout pass happened + //self.subnode.frame = CGRectMake(self.subnode.frame.origin.x, self.subnode.frame.origin.y, 100, self.calculatedSize.height); +} + @end diff --git a/examples/Kittens/Sample/KittenNode.mm b/examples/Kittens/Sample/KittenNode.mm index 67e586b4a7..42a94a055a 100644 --- a/examples/Kittens/Sample/KittenNode.mm +++ b/examples/Kittens/Sample/KittenNode.mm @@ -201,7 +201,7 @@ static const CGFloat kInnerPadding = 10.0f; - (void)toggleImageEnlargement { _isImageEnlarged = !_isImageEnlarged; - [self setNeedsLayout]; + [self invalidateSize]; } - (void)toggleNodesSwap @@ -211,7 +211,7 @@ static const CGFloat kInnerPadding = 10.0f; [UIView animateWithDuration:0.15 animations:^{ self.alpha = 0; } completion:^(BOOL finished) { - [self setNeedsLayout]; + [self invalidateSize]; [self.view layoutIfNeeded]; [UIView animateWithDuration:0.15 animations:^{