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 ASCellNode
@interface ASCellNode () <ASDisplayNodeSizingDelegate>
@interface ASCellNode ()
{
ASDisplayNodeViewControllerBlock _viewControllerBlock;
ASDisplayNodeDidLoadBlock _viewControllerDidLoadBlock;
@ -59,9 +59,6 @@ static NSMutableSet *__cellClassesForVisibilityNotifications = nil; // See +init
_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;
ASLayout *layout = [self layoutThatFits:self.constrainedSizeForCalculatedLayout];
CGSize newSize = [self sizeThatFits:CGSizeMake(CGRectGetWidth(self.bounds), CGFLOAT_MAX)];
// 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

View File

@ -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

View File

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

View File

@ -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<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;
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
@ -1513,6 +1465,8 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
return;
}
[self measureNodeWithBoundsIfNecessary:bounds];
// Handle placeholder layer creation in case the size of the node changed after the initial placeholder layer
// was created
if ([self _shouldHavePlaceholderLayer]) {
@ -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<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;
}
- (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

View File

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

View File

@ -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;

View File

@ -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];
}

View File

@ -135,7 +135,7 @@
ASDisplayNodeAssertMainThread();
[super layoutSublayers];
[self.asyncdisplaykit_node __layout];
[self.asyncdisplaykit_node __layoutSublayers];
}
- (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 (rasterizingFromAscendent) {
[self __layout];
[self __layoutSublayers];
}
// Capture these outside the display block so they are retained.

View File

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

View File

@ -21,7 +21,7 @@
#import "DetailRootNode.h"
#import "SampleSizingNode.h"
@interface DetailViewController () <ASDisplayNodeSizingDelegate>
@interface DetailViewController ()// <ASDisplayNodeSizingDelegate>
@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);
}

View File

@ -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

View File

@ -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:^{