diff --git a/AsyncDisplayKit/ASDisplayNode.h b/AsyncDisplayKit/ASDisplayNode.h index cb1594049a..f9ebca2af7 100644 --- a/AsyncDisplayKit/ASDisplayNode.h +++ b/AsyncDisplayKit/ASDisplayNode.h @@ -269,6 +269,7 @@ extern NSInteger const ASDefaultDrawingPriority; @property (nonatomic, readwrite, weak, nullable) id sizingDelegate; - (void)invalidateSize; +- (void)sizeToFit; - (CGSize)sizeThatFits:(CGSize)size; diff --git a/AsyncDisplayKit/ASDisplayNode.mm b/AsyncDisplayKit/ASDisplayNode.mm index dbe83499b8..fb3e4cb5eb 100644 --- a/AsyncDisplayKit/ASDisplayNode.mm +++ b/AsyncDisplayKit/ASDisplayNode.mm @@ -730,6 +730,36 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c) __instanceLock__.unlock(); } +- (void)sizeToFit +{ + ASDisplayNodeAssertThreadAffinity(self); + + __instanceLock__.lock(); + + [self setNeedsLayout]; + + CGSize maxSize = _supernode ? _supernode.bounds.size : CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX); + CGSize size = [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 }; + + // Frame's origin must be preserved. Since it is computed from bounds size, anchorPoint + // and position (see frame setter in ASDisplayNode+UIViewBridge), position needs to be adjusted. + CGPoint anchorPoint = self.anchorPoint; + CGPoint oldPosition = self.position; + CGFloat xDelta = (newSize.width - oldSize.width) * anchorPoint.x; + CGFloat yDelta = (newSize.height - oldSize.height) * anchorPoint.y; + self.position = CGPointMake(oldPosition.x + xDelta, oldPosition.y + yDelta); + } + + __instanceLock__.unlock(); +} + - (CGSize)sizeThatFits:(CGSize)size { return [self layoutThatFits:ASSizeRangeMake(CGSizeZero, size)].size; diff --git a/examples/ASViewController/Sample/DetailViewController.m b/examples/ASViewController/Sample/DetailViewController.m index e246ae56ab..02bdceb2f9 100644 --- a/examples/ASViewController/Sample/DetailViewController.m +++ b/examples/ASViewController/Sample/DetailViewController.m @@ -74,7 +74,6 @@ // Initial size of sizing node //self.sizingNode.frame = CGRectMake(100, 100, 50, 50); - self.buttonNode.frame = CGRectMake(100, 100, 200, 10); [self displayNodeDidInvalidateSize:self.buttonNode]; // Initial size for image node @@ -95,19 +94,32 @@ { [super viewDidLayoutSubviews]; + [self updateButtonNodeLayout]; + // Update the sizing node layout [self updateNodeLayout]; } #pragma mark - Update the node based on the new size +- (void)updateButtonNodeLayout +{ + [self.buttonNode sizeToFit]; + self.buttonNode.frame = CGRectMake((self.view.bounds.size.width - self.buttonNode.bounds.size.width) / 2.0, + 100, + self.buttonNode.bounds.size.width, + self.buttonNode.bounds.size.height); + + //CGSize s = [self.buttonNode sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; + //self.buttonNode.frame = CGRectMake(100, 100, s.width, s.height); +} + // 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) { - CGSize s = [self.buttonNode sizeThatFits:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX)]; - self.buttonNode.frame = CGRectMake(100, 100, s.width, s.height); + [self updateButtonNodeLayout]; return; }