Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 3725e53

Browse files
committed
Merge pull request #1687 from levi/constrainedSize
[ASDisplayNode] Move constrained size to ASLayout
2 parents 52d5899 + 3b6347c commit 3725e53

18 files changed

Lines changed: 128 additions & 71 deletions

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -616,25 +616,20 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
616616
[self cancelLayoutTransitionsInProgress];
617617

618618
ASLayout *previousLayout = _layout;
619-
ASSizeRange previousConstrainedSize = _constrainedSize;
620619
ASLayout *newLayout = [self calculateLayoutThatFits:constrainedSize];
621620

622621
if (ASHierarchyStateIncludesLayoutPending(_hierarchyState)) {
623622
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
624623
pendingLayout:newLayout
625-
pendingConstrainedSize:constrainedSize
626-
previousLayout:previousLayout
627-
previousConstrainedSize:previousConstrainedSize];
624+
previousLayout:previousLayout];
628625
} else {
629626
ASLayoutTransition *layoutContext;
630627
if (self.usesImplicitHierarchyManagement) {
631628
layoutContext = [[ASLayoutTransition alloc] initWithNode:self
632629
pendingLayout:newLayout
633-
pendingConstrainedSize:constrainedSize
634-
previousLayout:previousLayout
635-
previousConstrainedSize:previousConstrainedSize];
630+
previousLayout:previousLayout];
636631
}
637-
[self applyLayout:newLayout constrainedSize:constrainedSize layoutContext:layoutContext];
632+
[self applyLayout:newLayout layoutContext:layoutContext];
638633
[self _completeLayoutCalculation];
639634
}
640635

@@ -655,10 +650,15 @@ - (BOOL)shouldMeasureWithSizeRange:(ASSizeRange)constrainedSize
655650
}
656651
}
657652

658-
// only calculate the size if
659-
// - we haven't already
660-
// - the constrained size range is different
661-
return (!_flags.isMeasured || !ASSizeRangeEqualToSizeRange(constrainedSize, _constrainedSize));
653+
// Only generate a new layout if:
654+
// - The current layout is dirty
655+
// - The passed constrained size is different than the layout's constrained size
656+
return ([self _hasDirtyLayout] || !ASSizeRangeEqualToSizeRange(constrainedSize, _layout.constrainedSizeRange));
657+
}
658+
659+
- (BOOL)_hasDirtyLayout
660+
{
661+
return _layout == nil || _layout.isDirty;
662662
}
663663

664664
#pragma mark - Layout Transition
@@ -667,9 +667,8 @@ - (void)transitionLayoutWithAnimation:(BOOL)animated
667667
shouldMeasureAsync:(BOOL)shouldMeasureAsync
668668
measurementCompletion:(void(^)())completion
669669
{
670-
ASSizeRange currentConstrainedSize = _constrainedSize;
671670
[self invalidateCalculatedLayout];
672-
[self transitionLayoutWithSizeRange:currentConstrainedSize
671+
[self transitionLayoutWithSizeRange:_layout.constrainedSizeRange
673672
animated:animated
674673
shouldMeasureAsync:shouldMeasureAsync
675674
measurementCompletion:completion];
@@ -732,8 +731,7 @@ - (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize
732731
}
733732

734733
ASLayout *previousLayout = _layout;
735-
ASSizeRange previousConstrainedSize = _constrainedSize;
736-
[self applyLayout:newLayout constrainedSize:constrainedSize layoutContext:nil];
734+
[self applyLayout:newLayout layoutContext:nil];
737735

738736
ASDisplayNodePerformBlockOnEverySubnode(self, ^(ASDisplayNode * _Nonnull node) {
739737
[node applyPendingLayoutContext];
@@ -749,9 +747,7 @@ - (void)transitionLayoutWithSizeRange:(ASSizeRange)constrainedSize
749747

750748
_pendingLayoutTransition = [[ASLayoutTransition alloc] initWithNode:self
751749
pendingLayout:newLayout
752-
pendingConstrainedSize:constrainedSize
753-
previousLayout:previousLayout
754-
previousConstrainedSize:previousConstrainedSize];
750+
previousLayout:previousLayout];
755751
[_pendingLayoutTransition applySubnodeInsertions];
756752

757753
_transitionContext = [[_ASTransitionContext alloc] initWithAnimation:animated
@@ -1028,11 +1024,10 @@ - (void)__setNeedsLayout
10281024
ASDisplayNodeAssertThreadAffinity(self);
10291025
ASDN::MutexLocker l(_propertyLock);
10301026

1031-
if (!_flags.isMeasured) {
1027+
if ([self _hasDirtyLayout]) {
10321028
return;
10331029
}
10341030

1035-
ASSizeRange oldConstrainedSize = _constrainedSize;
10361031
[self invalidateCalculatedLayout];
10371032

10381033
if (_supernode) {
@@ -1045,7 +1040,7 @@ - (void)__setNeedsLayout
10451040
}
10461041

10471042
// This is the root node. Trigger a full measurement pass on *current* thread. Old constrained size is re-used.
1048-
[self measureWithSizeRange:oldConstrainedSize];
1043+
[self measureWithSizeRange:_layout.constrainedSizeRange];
10491044

10501045
CGRect oldBounds = self.bounds;
10511046
CGSize oldSize = oldBounds.size;
@@ -1105,7 +1100,7 @@ - (void)measureNodeWithBoundsIfNecessary:(CGRect)bounds
11051100
// Normally measure will be called before layout occurs. If this doesn't happen, nothing is going to call it at all.
11061101
// We simply call measureWithSizeRange: using a size range equal to whatever bounds were provided to that element or
11071102
// try to measure the node with the largest size as possible
1108-
if (self.supernode == nil && !self.supportsRangeManagedInterfaceState && !_flags.isMeasured) {
1103+
if (self.supernode == nil && !self.supportsRangeManagedInterfaceState && [self _hasDirtyLayout] == NO) {
11091104
if (CGRectEqualToRect(bounds, CGRectZero)) {
11101105
LOG(@"Warning: No size given for node before node was trying to layout itself: %@. Please provide a frame for the node.", self);
11111106
} else {
@@ -1118,7 +1113,7 @@ - (void)layout
11181113
{
11191114
ASDisplayNodeAssertMainThread();
11201115

1121-
if (!_flags.isMeasured) {
1116+
if ([self _hasDirtyLayout]) {
11221117
return;
11231118
}
11241119

@@ -1956,7 +1951,10 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
19561951
// Make sure layoutableObject of the root layout is `self`, so that the flattened layout will be structurally correct.
19571952
if (layout.layoutableObject != self) {
19581953
layout.position = CGPointZero;
1959-
layout = [ASLayout layoutWithLayoutableObject:self size:layout.size sublayouts:@[layout]];
1954+
layout = [ASLayout layoutWithLayoutableObject:self
1955+
constrainedSizeRange:constrainedSize
1956+
size:layout.size
1957+
sublayouts:@[layout]];
19601958
}
19611959
return [layout flattenedLayoutUsingPredicateBlock:^BOOL(ASLayout *evaluatedLayout) {
19621960
if (self.usesImplicitHierarchyManagement) {
@@ -1969,7 +1967,9 @@ - (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize
19691967
// If neither -layoutSpecThatFits: nor -calculateSizeThatFits: is overridden by subclassses, preferredFrameSize should be used,
19701968
// assume that the default implementation of -calculateSizeThatFits: returns it.
19711969
CGSize size = [self calculateSizeThatFits:constrainedSize.max];
1972-
return [ASLayout layoutWithLayoutableObject:self size:ASSizeRangeClamp(constrainedSize, size)];
1970+
return [ASLayout layoutWithLayoutableObject:self
1971+
constrainedSizeRange:constrainedSize
1972+
size:ASSizeRangeClamp(constrainedSize, size)];
19731973
}
19741974
}
19751975

@@ -2009,7 +2009,7 @@ - (CGSize)calculatedSize
20092009
- (ASSizeRange)constrainedSizeForCalculatedLayout
20102010
{
20112011
ASDN::MutexLocker l(_propertyLock);
2012-
return _constrainedSize;
2012+
return _layout.constrainedSizeRange;
20132013
}
20142014

20152015
- (void)setLayoutSpecBlock:(ASLayoutSpecBlock)layoutSpecBlock
@@ -2063,8 +2063,10 @@ - (UIImage *)placeholderImage
20632063
- (void)invalidateCalculatedLayout
20642064
{
20652065
ASDN::MutexLocker l(_propertyLock);
2066-
// This will cause -measureWithSizeRange: to actually compute the size instead of returning the previously cached size
2067-
_flags.isMeasured = NO;
2066+
2067+
// This will cause the next call to -measureWithSizeRange: to actually compute a new layout
2068+
// instead of returning the current layout
2069+
_layout.dirty = YES;
20682070
}
20692071

20702072
- (void)__didLoad
@@ -2395,26 +2397,19 @@ - (void)applyPendingLayoutContext
23952397
{
23962398
ASDN::MutexLocker l(_propertyLock);
23972399
if (_pendingLayoutTransition) {
2398-
[self applyLayout:_pendingLayoutTransition.pendingLayout
2399-
constrainedSize:_pendingLayoutTransition.pendingConstrainedSize
2400-
layoutContext:_pendingLayoutTransition];
2400+
[self applyLayout:_pendingLayoutTransition.pendingLayout layoutContext:_pendingLayoutTransition];
24012401
_pendingLayoutTransition = nil;
24022402
}
24032403
}
24042404

2405-
- (void)applyLayout:(ASLayout *)layout
2406-
constrainedSize:(ASSizeRange)constrainedSize
2407-
layoutContext:(ASLayoutTransition *)layoutContext
2405+
- (void)applyLayout:(ASLayout *)layout layoutContext:(ASLayoutTransition *)layoutContext
24082406
{
24092407
ASDN::MutexLocker l(_propertyLock);
24102408
_layout = layout;
24112409

24122410
ASDisplayNodeAssertTrue(layout.layoutableObject == self);
24132411
ASDisplayNodeAssertTrue(layout.size.width >= 0.0);
24142412
ASDisplayNodeAssertTrue(layout.size.height >= 0.0);
2415-
2416-
_constrainedSize = constrainedSize;
2417-
_flags.isMeasured = YES;
24182413

24192414
if (self.usesImplicitHierarchyManagement && layoutContext != nil) {
24202415
[layoutContext applySubnodeInsertions];

AsyncDisplayKit/Layout/ASBackgroundLayoutSpec.mm

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
5555
contentsLayout.position = CGPointZero;
5656
[sublayouts addObject:contentsLayout];
5757

58-
return [ASLayout layoutWithLayoutableObject:self size:contentsLayout.size sublayouts:sublayouts];
58+
return [ASLayout layoutWithLayoutableObject:self
59+
constrainedSizeRange:constrainedSize
60+
size:contentsLayout.size
61+
sublayouts:sublayouts];
5962
}
6063

6164
- (void)setBackground:(id<ASLayoutable>)background

AsyncDisplayKit/Layout/ASInsetLayoutSpec.mm

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
9191

9292
if (self.child == nil) {
9393
ASDisplayNodeAssert(NO, @"Inset spec measured without a child. The spec will do nothing.");
94-
return [ASLayout layoutWithLayoutableObject:self size:CGSizeZero];
94+
return [ASLayout layoutWithLayoutableObject:self
95+
constrainedSizeRange:constrainedSize
96+
size:CGSizeZero];
9597
}
9698

9799
ASLayout *sublayout = [self.child measureWithSizeRange:insetConstrainedSize];
@@ -112,7 +114,10 @@ - (ASLayout *)measureWithSizeRange:(ASSizeRange)constrainedSize
112114

113115
sublayout.position = CGPointMake(x, y);
114116

115-
return [ASLayout layoutWithLayoutableObject:self size:computedSize sublayouts:@[sublayout]];
117+
return [ASLayout layoutWithLayoutableObject:self
118+
constrainedSizeRange:constrainedSize
119+
size:computedSize
120+
sublayouts:@[sublayout]];
116121
}
117122

118123
@end

AsyncDisplayKit/Layout/ASLayout.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@
1313
#import <UIKit/UIKit.h>
1414
#import <AsyncDisplayKit/ASAssert.h>
1515
#import <AsyncDisplayKit/ASLayoutable.h>
16+
#import <AsyncDisplayKit/ASDimension.h>
1617

1718
NS_ASSUME_NONNULL_BEGIN
1819

1920
extern CGPoint const CGPointNull;
2021

2122
extern BOOL CGPointIsNull(CGPoint point);
2223

23-
/** Represents a computed immutable layout tree. */
24+
/**
25+
* A node in the layout tree that represents the size and position of the object that created it (ASLayoutable).
26+
*/
2427
@interface ASLayout : NSObject
2528

2629
/**
@@ -40,6 +43,11 @@ extern BOOL CGPointIsNull(CGPoint point);
4043
*/
4144
@property (nonatomic, readwrite) CGPoint position;
4245

46+
/**
47+
* The size range that was use to determine the size of the layout.
48+
*/
49+
@property (nonatomic, readonly) ASSizeRange constrainedSizeRange;
50+
4351
/**
4452
* Array of ASLayouts. Each must have a valid non-null position.
4553
*/
@@ -50,6 +58,11 @@ extern BOOL CGPointIsNull(CGPoint point);
5058
*/
5159
@property (nonatomic, readonly) NSArray<ASLayout *> *immediateSublayouts;
5260

61+
/**
62+
* Mark the layout dirty for future regeneration.
63+
*/
64+
@property (nonatomic, getter=isDirty) BOOL dirty;
65+
5366
/**
5467
* A boolean describing if the current layout has been flattened.
5568
*/
@@ -67,6 +80,7 @@ extern BOOL CGPointIsNull(CGPoint point);
6780
* @param sublayouts Sublayouts belong to the new layout.
6881
*/
6982
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
83+
constrainedSizeRange:(ASSizeRange)sizeRange
7084
size:(CGSize)size
7185
position:(CGPoint)position
7286
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
@@ -85,6 +99,7 @@ extern BOOL CGPointIsNull(CGPoint point);
8599
* @param sublayouts Sublayouts belong to the new layout.
86100
*/
87101
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
102+
constrainedSizeRange:(ASSizeRange)sizeRange
88103
size:(CGSize)size
89104
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
90105

@@ -97,7 +112,9 @@ extern BOOL CGPointIsNull(CGPoint point);
97112
*
98113
* @param size The size of this layout.
99114
*/
100-
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject size:(CGSize)size;
115+
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
116+
constrainedSizeRange:(ASSizeRange)sizeRange
117+
size:(CGSize)size;
101118

102119
/**
103120
* Convenience initializer that is flattened and has CGPointNull position.
@@ -109,6 +126,7 @@ extern BOOL CGPointIsNull(CGPoint point);
109126
* @param sublayouts Sublayouts belong to the new layout.
110127
*/
111128
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
129+
constrainedSizeRange:(ASSizeRange)sizeRange
112130
size:(CGSize)size
113131
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts;
114132

AsyncDisplayKit/Layout/ASLayout.mm

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import "ASAssert.h"
1313
#import "ASLayoutSpecUtilities.h"
1414
#import "ASInternalHelpers.h"
15+
#import "ASDimension.h"
1516
#import <queue>
1617

1718
CGPoint const CGPointNull = {NAN, NAN};
@@ -24,6 +25,7 @@ extern BOOL CGPointIsNull(CGPoint point)
2425
@implementation ASLayout
2526

2627
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
28+
constrainedSizeRange:(ASSizeRange)sizeRange
2729
size:(CGSize)size
2830
position:(CGPoint)position
2931
sublayouts:(NSArray *)sublayouts
@@ -46,7 +48,9 @@ + (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
4648
} else {
4749
size = CGSizeMake(ASCeilPixelValue(size.width), ASCeilPixelValue(size.height));
4850
}
51+
l->_constrainedSizeRange = sizeRange;
4952
l->_size = size;
53+
l->_dirty = NO;
5054

5155
if (CGPointIsNull(position) == NO) {
5256
l->_position = CGPointMake(ASCeilPixelValue(position.x), ASCeilPixelValue(position.y));
@@ -68,22 +72,39 @@ + (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
6872
}
6973

7074
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
75+
constrainedSizeRange:(ASSizeRange)sizeRange
7176
size:(CGSize)size
7277
sublayouts:(NSArray *)sublayouts
7378
{
74-
return [self layoutWithLayoutableObject:layoutableObject size:size position:CGPointNull sublayouts:sublayouts flattened:NO];
79+
return [self layoutWithLayoutableObject:layoutableObject
80+
constrainedSizeRange:sizeRange
81+
size:size
82+
position:CGPointNull
83+
sublayouts:sublayouts
84+
flattened:NO];
7585
}
7686

77-
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject size:(CGSize)size
87+
+ (instancetype)layoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
88+
constrainedSizeRange:(ASSizeRange)sizeRange
89+
size:(CGSize)size
7890
{
79-
return [self layoutWithLayoutableObject:layoutableObject size:size sublayouts:nil];
91+
return [self layoutWithLayoutableObject:layoutableObject
92+
constrainedSizeRange:sizeRange
93+
size:size
94+
sublayouts:nil];
8095
}
8196

8297
+ (instancetype)flattenedLayoutWithLayoutableObject:(id<ASLayoutable>)layoutableObject
98+
constrainedSizeRange:(ASSizeRange)sizeRange
8399
size:(CGSize)size
84100
sublayouts:(nullable NSArray<ASLayout *> *)sublayouts
85101
{
86-
return [self layoutWithLayoutableObject:layoutableObject size:size position:CGPointNull sublayouts:sublayouts flattened:YES];
102+
return [self layoutWithLayoutableObject:layoutableObject
103+
constrainedSizeRange:sizeRange
104+
size:size
105+
position:CGPointNull
106+
sublayouts:sublayouts
107+
flattened:YES];
87108
}
88109

89110
- (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *))predicateBlock
@@ -110,6 +131,7 @@ - (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *))predicate
110131

111132
if (predicateBlock(context.layout)) {
112133
[flattenedSublayouts addObject:[ASLayout layoutWithLayoutableObject:context.layout.layoutableObject
134+
constrainedSizeRange:context.layout.constrainedSizeRange
113135
size:context.layout.size
114136
position:context.absolutePosition
115137
sublayouts:nil
@@ -124,7 +146,10 @@ - (ASLayout *)flattenedLayoutUsingPredicateBlock:(BOOL (^)(ASLayout *))predicate
124146
}
125147
}
126148

127-
return [ASLayout flattenedLayoutWithLayoutableObject:_layoutableObject size:_size sublayouts:flattenedSublayouts];
149+
return [ASLayout flattenedLayoutWithLayoutableObject:_layoutableObject
150+
constrainedSizeRange:_constrainedSizeRange
151+
size:_size
152+
sublayouts:flattenedSublayouts];
128153
}
129154

130155
- (CGRect)frame

0 commit comments

Comments
 (0)