Some more changes

This commit is contained in:
Michael Schneider 2016-11-13 17:39:46 -08:00
parent c819d716b4
commit 0eb882bae5
13 changed files with 181 additions and 166 deletions

View File

@ -29,7 +29,7 @@
#pragma mark - #pragma mark -
#pragma mark ASCellNode #pragma mark ASCellNode
@interface ASCellNode () <ASDisplayNodeSizingDelegate> @interface ASCellNode ()
{ {
ASDisplayNodeViewControllerBlock _viewControllerBlock; ASDisplayNodeViewControllerBlock _viewControllerBlock;
ASDisplayNodeDidLoadBlock _viewControllerDidLoadBlock; ASDisplayNodeDidLoadBlock _viewControllerDidLoadBlock;
@ -59,9 +59,6 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
_selectionStyle = UITableViewCellSelectionStyleDefault; _selectionStyle = UITableViewCellSelectionStyleDefault;
self.clipsToBounds = YES; self.clipsToBounds = YES;
// ASCellNode acts as sizing container for the node
self.sizingDelegate = self;
return self; return self;
} }
@ -121,19 +118,16 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
_viewControllerNode.frame = self.bounds; _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 oldSize = self.calculatedSize;
ASLayout *layout = [self layoutThatFits:self.constrainedSizeForCalculatedLayout]; CGSize newSize = [self sizeThatFits:CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX)];
// TODO: Needs proper adjustment if (CGSizeEqualToSize(oldSize, newSize) == NO) {
CGRect f = self.frame; self.frame = {self.frame.origin, newSize};
f.size = layout.size; [self didRelayoutFromOldSize:oldSize toNewSize:newSize];
self.frame = f; }
[self didRelayoutFromOldSize:oldSize toNewSize:layout.size];
} }
- (void)transitionLayoutWithAnimation:(BOOL)animated - (void)transitionLayoutWithAnimation:(BOOL)animated

View File

@ -765,7 +765,9 @@ static NSString * const kCellReuseIdentifier = @"_ASCollectionViewCell";
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath - (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 - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath

View File

@ -267,8 +267,9 @@ extern NSInteger const ASDefaultDrawingPriority;
/** @name Managing dimensions */ /** @name Managing dimensions */
@property (nonatomic, readwrite, weak, nullable) id<ASDisplayNodeSizingDelegate> sizingDelegate; //@property (nonatomic, readwrite, weak, nullable) id<ASDisplayNodeSizingDelegate> sizingDelegate;
- (void)invalidateSize; - (void)invalidateSize;
- (void)didInvalidateSize;
- (void)sizeToFit; - (void)sizeToFit;
- (CGSize)sizeThatFits:(CGSize)size; - (CGSize)sizeThatFits:(CGSize)size;

View File

@ -709,14 +709,18 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
__instanceLock__.lock(); __instanceLock__.lock();
// Mark the node for layout in the next layout pass
[self invalidateCalculatedLayout]; [self invalidateCalculatedLayout];
// This is the root node. Let the delegate know that the size changed // 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 // 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 // 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:)]) { // if ([self.sizingDelegate respondsToSelector:@selector(displayNodeDidInvalidateSize:)]) {
[self.sizingDelegate displayNodeDidInvalidateSize:self]; // [self.sizingDelegate displayNodeDidInvalidateSize:self];
} // }
// Hook for subclasses to get size invalidation changes
[self didInvalidateSize];
if (_supernode) { if (_supernode) {
ASDisplayNode *supernode = _supernode; ASDisplayNode *supernode = _supernode;
@ -727,9 +731,17 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return; return;
} }
// We are now at the root node trigger a layout pass
[self setNeedsLayout];
__instanceLock__.unlock(); __instanceLock__.unlock();
} }
- (void)didInvalidateSize
{
}
- (void)sizeToFit - (void)sizeToFit
{ {
ASDisplayNodeAssertThreadAffinity(self); ASDisplayNodeAssertThreadAffinity(self);
@ -739,11 +751,10 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self setNeedsLayout]; [self setNeedsLayout];
CGSize maxSize = _supernode ? _supernode.bounds.size : CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX); 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; CGRect oldBounds = self.bounds;
CGSize oldSize = oldBounds.size; CGSize oldSize = oldBounds.size;
CGSize newSize = _calculatedDisplayNodeLayout->layout.size;
if (! CGSizeEqualToSize(oldSize, newSize)) { if (! CGSizeEqualToSize(oldSize, newSize)) {
self.bounds = (CGRect){ oldBounds.origin, newSize }; self.bounds = (CGRect){ oldBounds.origin, newSize };
@ -777,74 +788,12 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
{ {
ASDN::MutexLocker l(__instanceLock__); 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); ASDisplayNodeAssertNotNil(_calculatedDisplayNodeLayout->layout, @"-[ASDisplayNode layoutThatFits:parentSize:] _layout should not be nil! %@", self);
return _calculatedDisplayNodeLayout->layout ? : [ASLayout layoutWithLayoutElement:self size:{0, 0}]; return _calculatedDisplayNodeLayout->layout ? : [ASLayout layoutWithLayoutElement:self size:{0, 0}];
} }
[self cancelLayoutTransition]; return [self calculateLayoutThatFits:constrainedSize restrictedToSize:self.style.size relativeToParentSize:parentSize];
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<ASDisplayNodeLayout>(
[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;
} }
- (ASLayoutElementType)layoutElementType - (ASLayoutElementType)layoutElementType
@ -883,7 +832,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return; return;
} }
[self invalidateCalculatedLayout]; [self setNeedsLayout];
[self transitionLayoutWithSizeRange:_calculatedDisplayNodeLayout->constrainedSize [self transitionLayoutWithSizeRange:_calculatedDisplayNodeLayout->constrainedSize
animated:animated animated:animated
shouldMeasureAsync:shouldMeasureAsync 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 // Passed constrainedSize is the the same as the node's current constrained size it's a noop
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
if ([self shouldCalculateLayoutWithConstrainedSize:constrainedSize parentSize:constrainedSize.max] == NO) { if (_calculatedDisplayNodeLayout->isValidForConstrainedSizeParentSize(constrainedSize, constrainedSize.max)) {
return; return;
} }
@ -1449,13 +1398,18 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self displayImmediately]; [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 - (void)__setNeedsLayout
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
// This will cause the next call to -layoutThatFits:parentSize: to compute a new layout instead of returning [self invalidateCalculatedLayout];
// the cached layout in case the constrained or parent size did not change
_calculatedDisplayNodeLayout->invalidate();
} }
/** /**
@ -1482,7 +1436,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[supernode __layoutIfNeeded]; [supernode __layoutIfNeeded];
} else { } else {
// Layout all subviews starting from the first node that needs layout // 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. // 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(); ASDisplayNodeAssertMainThread();
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
CGRect bounds = self.bounds; CGRect bounds = _threadSafeBounds;
[self measureNodeWithBoundsIfNecessary:bounds];
if (CGRectEqualToRect(bounds, CGRectZero)) { if (CGRectEqualToRect(bounds, CGRectZero)) {
// Performing layout on a zero-bounds view often results in frame calculations // Performing layout on a zero-bounds view often results in frame calculations
@ -1513,6 +1465,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return; return;
} }
[self measureNodeWithBoundsIfNecessary:bounds];
// Handle placeholder layer creation in case the size of the node changed after the initial placeholder layer // Handle placeholder layer creation in case the size of the node changed after the initial placeholder layer
// was created // was created
if ([self _shouldHavePlaceholderLayer]) { if ([self _shouldHavePlaceholderLayer]) {
@ -1524,18 +1478,9 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
[self layoutDidFinish]; [self layoutDidFinish];
} }
/// Needs to be called with lock held
- (void)measureNodeWithBoundsIfNecessary:(CGRect)bounds - (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 // 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 // the layout transition
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) { 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 // for the node using a size range equal to whatever bounds were provided to the node
if (CGRectEqualToRect(bounds, CGRectZero)) { if (CGRectEqualToRect(bounds, CGRectZero)) {
LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self); LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
} else if (hasDirtyLayout || CGSizeEqualToSize(calculatedLayoutSize, bounds.size) == NO) { return;
[self layoutThatFits:ASSizeRangeMake(bounds.size)]; }
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<ASDisplayNodeLayout>(
[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; 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 - (void)__didLoad
{ {
ASDN::MutexLocker l(__instanceLock__); ASDN::MutexLocker l(__instanceLock__);
@ -3730,7 +3718,7 @@ static const char *ASDisplayNodeAssociatedNodeKey = "ASAssociatedNode";
{ {
// Deprecated preferredFrameSize just calls through to set width and height // Deprecated preferredFrameSize just calls through to set width and height
self.style.preferredSize = preferredFrameSize; self.style.preferredSize = preferredFrameSize;
[self invalidateCalculatedLayout]; [self setNeedsLayout];
} }
- (CGSize)preferredFrameSize - (CGSize)preferredFrameSize

View File

@ -204,7 +204,7 @@ struct ASImageNodeDrawParameters {
if (!ASObjectIsEqual(_image, image)) { if (!ASObjectIsEqual(_image, image)) {
_image = image; _image = image;
[self invalidateCalculatedLayout]; [self setNeedsLayout];
if (image) { if (image) {
[self setNeedsDisplay]; [self setNeedsDisplay];

View File

@ -751,6 +751,7 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{ {
ASCellNode *node = [_dataController nodeAtIndexPath:indexPath]; ASCellNode *node = [_dataController nodeAtIndexPath:indexPath];
[node layoutIfNeeded];
return node.calculatedSize.height; return node.calculatedSize.height;
} }
@ -1509,8 +1510,6 @@ static NSString * const kCellReuseIdentifier = @"_ASTableViewCell";
#pragma mark - _ASTableViewCellDelegate #pragma mark - _ASTableViewCellDelegate
#pragma mark - _ASTableViewCellDelegate
- (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell - (void)didLayoutSubviewsOfTableViewCell:(_ASTableViewCell *)tableViewCell
{ {
ASCellNode *node = tableViewCell.node; 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). // 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, // 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. // 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.min.width = contentViewWidth;
constrainedSize.max.width = contentViewWidth; constrainedSize.max.width = contentViewWidth;

View File

@ -330,8 +330,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(textContainerInset, _textContainerInset); BOOL needsUpdate = !UIEdgeInsetsEqualToEdgeInsets(textContainerInset, _textContainerInset);
if (needsUpdate) { if (needsUpdate) {
_textContainerInset = textContainerInset; _textContainerInset = textContainerInset;
[self invalidateCalculatedLayout]; [self invalidateSize];
[self setNeedsLayout];
} }
} }
@ -487,7 +486,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
} }
// Tell the display node superclasses that the cached layout is incorrect now // 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 // Force display to create renderer with new size and redisplay with new string
[self setNeedsDisplay]; [self setNeedsDisplay];
@ -510,7 +509,7 @@ static NSArray *DefaultLinkAttributeNames = @[ NSLinkAttributeName ];
_exclusionPaths = [exclusionPaths copy]; _exclusionPaths = [exclusionPaths copy];
[self _invalidateRenderer]; [self _invalidateRenderer];
[self invalidateCalculatedLayout]; [self invalidateSize];
[self setNeedsDisplay]; [self setNeedsDisplay];
} }

View File

@ -135,7 +135,7 @@
ASDisplayNodeAssertMainThread(); ASDisplayNodeAssertMainThread();
[super layoutSublayers]; [super layoutSublayers];
[self.asyncdisplaykit_node __layout]; [self.asyncdisplaykit_node __layoutSublayers];
} }
- (void)setNeedsDisplay - (void)setNeedsDisplay

View File

@ -52,7 +52,7 @@
// if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers // if super node is rasterizing descendants, subnodes will not have had layout calls because they don't have layers
if (rasterizingFromAscendent) { if (rasterizingFromAscendent) {
[self __layout]; [self __layoutSublayers];
} }
// Capture these outside the display block so they are retained. // Capture these outside the display block so they are retained.

View File

@ -212,7 +212,7 @@ FOUNDATION_EXPORT NSString * const ASRenderingEngineDidDisplayNodesScheduledBefo
*/ */
- (void)__setNeedsDisplay; - (void)__setNeedsDisplay;
- (void)__layout; - (void)__layoutSublayers;
- (void)__setSupernode:(ASDisplayNode *)supernode; - (void)__setSupernode:(ASDisplayNode *)supernode;
/** /**

View File

@ -21,7 +21,7 @@
#import "DetailRootNode.h" #import "DetailRootNode.h"
#import "SampleSizingNode.h" #import "SampleSizingNode.h"
@interface DetailViewController () <ASDisplayNodeSizingDelegate> @interface DetailViewController ()// <ASDisplayNodeSizingDelegate>
@property (strong, nonatomic) SampleSizingNode *sizingNode; @property (strong, nonatomic) SampleSizingNode *sizingNode;
@property (strong, nonatomic) ASNetworkImageNode *imageNode; @property (strong, nonatomic) ASNetworkImageNode *imageNode;
@ -40,7 +40,7 @@
// Set the sizing delegate of the root node to the container // Set the sizing delegate of the root node to the container
_sizingNode = [SampleSizingNode new]; _sizingNode = [SampleSizingNode new];
_sizingNode.autoresizingMask = UIViewAutoresizingNone; _sizingNode.autoresizingMask = UIViewAutoresizingNone;
_sizingNode.sizingDelegate = self; //_sizingNode.sizingDelegate = self;
_imageNode = [ASNetworkImageNode new]; _imageNode = [ASNetworkImageNode new];
_imageNode.needsDisplayOnBoundsChange = YES; _imageNode.needsDisplayOnBoundsChange = YES;
@ -53,7 +53,7 @@
[_buttonNode setTitle:@"Some Title" withFont:nil withColor:nil forState:ASControlStateNormal]; [_buttonNode setTitle:@"Some Title" withFont:nil withColor:nil forState:ASControlStateNormal];
[_buttonNode setTitle:@"Some Bla" withFont:nil withColor:[UIColor orangeColor] forState:ASControlStateHighlighted]; [_buttonNode setTitle:@"Some Bla" withFont:nil withColor:[UIColor orangeColor] forState:ASControlStateHighlighted];
[_buttonNode addTarget:self action:@selector(buttonAction:) forControlEvents:ASControlNodeEventTouchUpInside]; [_buttonNode addTarget:self action:@selector(buttonAction:) forControlEvents:ASControlNodeEventTouchUpInside];
_buttonNode.sizingDelegate = self; //_buttonNode.sizingDelegate = self;
return self; return self;
} }
@ -74,7 +74,7 @@
// Initial size of sizing node // Initial size of sizing node
//self.sizingNode.frame = CGRectMake(100, 100, 50, 50); //self.sizingNode.frame = CGRectMake(100, 100, 50, 50);
[self displayNodeDidInvalidateSize:self.buttonNode]; //[self displayNodeDidInvalidateSize:self.buttonNode];
// Initial size for image node // Initial size for image node
// self.imageNode.frame = CGRectMake(50, 70, 100, 100); // self.imageNode.frame = CGRectMake(50, 70, 100, 100);
@ -94,6 +94,7 @@
{ {
[super viewDidLayoutSubviews]; [super viewDidLayoutSubviews];
// Updat the sizing for the button node
[self updateButtonNodeLayout]; [self updateButtonNodeLayout];
// Update the sizing node layout // 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 // 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 // to get the new size from the display node and update the frame based on the returned size
- (void)displayNodeDidInvalidateSize:(ASDisplayNode *)displayNode //- (void)displayNodeDidInvalidateSize:(ASDisplayNode *)displayNode
{ //{
if (displayNode == self.buttonNode) { // if (displayNode == self.buttonNode) {
[self updateButtonNodeLayout]; // [self updateButtonNodeLayout];
return; // return;
} // }
//
[self updateNodeLayout]; // [self updateNodeLayout];
//
/*dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // /*dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self updateNodeLayoutRandom]; // [self updateNodeLayoutRandom];
});*/ // });*/
//
/*[NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) { // /*[NSTimer scheduledTimerWithTimeInterval:2.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[self updateNodeLayoutRandom]; // [self updateNodeLayoutRandom];
}];*/ // }];*/
} //}
- (void)updateNodeLayout - (void)updateNodeLayout
{ {
@ -140,29 +141,34 @@
//return; //return;
// Use the bounds of the view and get the fitting size // 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 // 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; //size.width -= 10;
//[self.sizingNode setNeedsLayout]; //[self.sizingNode setNeedsLayout];
self.sizingNode.frame = CGRectMake((self.view.bounds.size.width - size.width) / 2.0, self.sizingNode.frame = CGRectMake((CGRectGetWidth(self.view.bounds) - size.width) / 2.0,
(self.view.bounds.size.height - size.height) / 2.0, (CGRectGetHeight(self.view.bounds) - size.height) / 2.0,
size.width, size.height); size.width,
size.height);
//dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //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); self.sizingNode.frame = CGRectInset(self.sizingNode.frame, 10, 10);
//}); //});
} }
- (void)updateNodeLayoutRandom - (void)updateNodeLayoutRandom
{ {
CGRect bounds = self.view.bounds;
// Pick a randome width and height and set the frame of the node // Pick a randome width and height and set the frame of the node
CGSize size = CGSizeZero; CGSize size = CGSizeZero;
size.width = arc4random_uniform(self.view.bounds.size.width); size.width = arc4random_uniform(CGRectGetWidth(bounds));
size.height = arc4random_uniform(self.view.bounds.size.height); size.height = arc4random_uniform(CGRectGetHeight(bounds));
//[self.sizingNode setNeedsLayout]; //[self.sizingNode setNeedsLayout];
self.sizingNode.frame = CGRectMake((self.view.bounds.size.width - size.width) / 2.0, self.sizingNode.frame = CGRectMake((CGRectGetWidth(bounds) - size.width) / 2.0,
(self.view.bounds.size.height - size.height) / 2.0, (CGRectGetHeight(bounds) - size.height) / 2.0,
size.width, size.height); size.width,
size.height);
} }

View File

@ -8,10 +8,27 @@
#import "SampleSizingNode.h" #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 () @interface SampleSizingNode ()
@property (nonatomic, strong) ASDisplayNode *subnode;
@property (nonatomic, assign) NSInteger state; @property (nonatomic, assign) NSInteger state;
@property (nonatomic, strong) SampleSizingNodeSubnode *subnode;
@property (nonatomic, strong) ASTextNode *textNode; @property (nonatomic, strong) ASTextNode *textNode;
@property (nonatomic, strong) ASNetworkImageNode *imageNode; @property (nonatomic, strong) ASNetworkImageNode *imageNode;
@end @end
@ -35,13 +52,15 @@
_imageNode.backgroundColor = [UIColor brownColor]; _imageNode.backgroundColor = [UIColor brownColor];
_imageNode.needsDisplayOnBoundsChange = YES; _imageNode.needsDisplayOnBoundsChange = YES;
_imageNode.style.height = ASDimensionMakeWithFraction(1.0); _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.backgroundColor = [UIColor redColor];
_subnode.automaticallyManagesSubnodes = YES; _subnode.automaticallyManagesSubnodes = YES;
// Layout description via layoutSpecBlock
__weak __typeof(self) weakSelf = self; __weak __typeof(self) weakSelf = self;
_subnode.layoutSpecBlock = ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) { _subnode.layoutSpecBlock = ^ASLayoutSpec *(__kindof ASDisplayNode * _Nonnull node, ASSizeRange constrainedSize) {
@ -113,7 +132,6 @@
[self invalidateSize]; [self invalidateSize];
} }
#pragma mark - ASDisplayNode #pragma mark - ASDisplayNode
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize - (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
@ -129,5 +147,13 @@
child:self.subnode]; 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 @end

View File

@ -201,7 +201,7 @@ static const CGFloat kInnerPadding = 10.0f;
- (void)toggleImageEnlargement - (void)toggleImageEnlargement
{ {
_isImageEnlarged = !_isImageEnlarged; _isImageEnlarged = !_isImageEnlarged;
[self setNeedsLayout]; [self invalidateSize];
} }
- (void)toggleNodesSwap - (void)toggleNodesSwap
@ -211,7 +211,7 @@ static const CGFloat kInnerPadding = 10.0f;
[UIView animateWithDuration:0.15 animations:^{ [UIView animateWithDuration:0.15 animations:^{
self.alpha = 0; self.alpha = 0;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
[self setNeedsLayout]; [self invalidateSize];
[self.view layoutIfNeeded]; [self.view layoutIfNeeded];
[UIView animateWithDuration:0.15 animations:^{ [UIView animateWithDuration:0.15 animations:^{