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

Commit bbc0452

Browse files
author
Scott Goodson
committed
Improve UIView & CALayer handling of -addSubnode:, and ensure node hierarchies are hooked up even when addSubview: is used directly.
1 parent 8ce0f2a commit bbc0452

2 files changed

Lines changed: 91 additions & 32 deletions

File tree

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,17 +1225,24 @@ - (void)removeFromSupernode
12251225
if (!_supernode)
12261226
return;
12271227

1228-
// Do this before removing the view from the hierarchy, as the node will clear its supernode pointer when its view is removed from the hierarchy.
1229-
[_supernode _removeSubnode:self];
1230-
1231-
if (ASDisplayNodeThreadIsMain()) {
1228+
// Check to ensure that our view or layer is actually inside of our supernode; otherwise, don't remove it.
1229+
// Though _ASDisplayView decouples the supernode if it is inserted inside another view hierarchy, this is
1230+
// more difficult to guarantee with _ASDisplayLayer because CoreAnimation doesn't have a -didMoveToSuperlayer.
1231+
BOOL shouldRemoveFromSuperviewOrSuperlayer = NO;
1232+
1233+
if (self.nodeLoaded && _supernode.nodeLoaded) {
12321234
if (_flags.layerBacked) {
1233-
[_layer removeFromSuperlayer];
1235+
shouldRemoveFromSuperviewOrSuperlayer = (_layer.superlayer == _supernode.layer);
12341236
} else {
1235-
[_view removeFromSuperview];
1237+
shouldRemoveFromSuperviewOrSuperlayer = (_view.superview == _supernode.view);
12361238
}
1237-
} else {
1238-
dispatch_async(dispatch_get_main_queue(), ^{
1239+
}
1240+
1241+
// Do this before removing the view from the hierarchy, as the node will clear its supernode pointer when its view is removed from the hierarchy.
1242+
[_supernode _removeSubnode:self];
1243+
1244+
if (shouldRemoveFromSuperviewOrSuperlayer) {
1245+
ASPerformBlockOnMainThread(^{
12391246
if (_flags.layerBacked) {
12401247
[_layer removeFromSuperlayer];
12411248
} else {
@@ -1301,7 +1308,7 @@ - (void)__enterHierarchy
13011308
_flags.isEnteringHierarchy = NO;
13021309

13031310
CALayer *layer = self.layer;
1304-
if (!self.layer.contents) {
1311+
if (!layer.contents) {
13051312
[layer setNeedsDisplay];
13061313
}
13071314
}
@@ -2317,22 +2324,33 @@ @implementation CALayer (ASDisplayNodeInternal)
23172324

23182325
@implementation UIView (AsyncDisplayKit)
23192326

2320-
- (void)addSubnode:(ASDisplayNode *)node
2327+
- (void)addSubnode:(ASDisplayNode *)subnode
23212328
{
2322-
if (node.layerBacked) {
2323-
[self.layer addSublayer:node.layer];
2329+
if (subnode.layerBacked) {
2330+
// Call -addSubnode: so that we use the asyncdisplaykit_node path if possible.
2331+
[self.layer addSubnode:subnode];
23242332
} else {
2325-
[self addSubview:node.view];
2333+
ASDisplayNode *selfNode = self.asyncdisplaykit_node;
2334+
if (selfNode) {
2335+
[selfNode addSubnode:subnode];
2336+
} else {
2337+
[self addSubview:subnode.view];
2338+
}
23262339
}
23272340
}
23282341

23292342
@end
23302343

23312344
@implementation CALayer (AsyncDisplayKit)
23322345

2333-
- (void)addSubnode:(ASDisplayNode *)node
2346+
- (void)addSubnode:(ASDisplayNode *)subnode
23342347
{
2335-
[self addSublayer:node.layer];
2348+
ASDisplayNode *selfNode = self.asyncdisplaykit_node;
2349+
if (selfNode) {
2350+
[selfNode addSubnode:subnode];
2351+
} else {
2352+
[self addSublayer:subnode.layer];
2353+
}
23362354
}
23372355

23382356
@end

AsyncDisplayKit/Details/_ASDisplayView.mm

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ - (id)initWithFrame:(CGRect)frame
6363
return self;
6464
}
6565

66+
- (void)willMoveToWindow:(UIWindow *)newWindow
67+
{
68+
BOOL visible = (newWindow != nil);
69+
if (visible && !_node.inHierarchy) {
70+
[_node __enterHierarchy];
71+
}
72+
}
73+
74+
- (void)didMoveToWindow
75+
{
76+
BOOL visible = (self.window != nil);
77+
if (!visible && _node.inHierarchy) {
78+
[_node __exitHierarchy];
79+
}
80+
}
81+
6682
- (void)willMoveToSuperview:(UIView *)newSuperview
6783
{
6884
// Keep the node alive while the view is in a view hierarchy. This helps ensure that async-drawing views can always
@@ -76,28 +92,53 @@ - (void)willMoveToSuperview:(UIView *)newSuperview
7692
else if (currentSuperview && !newSuperview) {
7793
self.keepalive_node = nil;
7894
}
79-
}
80-
81-
- (void)willMoveToWindow:(UIWindow *)newWindow
82-
{
83-
BOOL visible = newWindow != nil;
84-
if (visible && !_node.inHierarchy) {
85-
[_node __enterHierarchy];
86-
} else if (!visible && _node.inHierarchy) {
87-
[_node __exitHierarchy];
95+
96+
if (newSuperview) {
97+
ASDisplayNode *supernode = _node.supernode;
98+
BOOL supernodeLoaded = supernode.nodeLoaded;
99+
ASDisplayNodeAssert(!supernode.isLayerBacked, @"Shouldn't be possible for _ASDisplayView's supernode to be layer-backed.");
100+
101+
BOOL needsSupernodeUpdate = NO;
102+
103+
if (supernode) {
104+
// If we have a supernode, compensate for users directly messing with views by updating to any new supernode.
105+
needsSupernodeUpdate = (!supernodeLoaded || supernode.view != newSuperview);
106+
} else {
107+
// If we have no supernode and we are now in a view hierarchy, check to see if we can hook up to a supernode.
108+
needsSupernodeUpdate = (newSuperview != nil);
109+
}
110+
111+
if (needsSupernodeUpdate) {
112+
// -removeFromSupernode is called by -addSubnode:, if it is needed.
113+
[newSuperview.asyncdisplaykit_node addSubnode:_node];
114+
}
88115
}
116+
89117
}
90118

91119
- (void)didMoveToSuperview
92120
{
93-
// FIXME maybe move this logic into ASDisplayNode addSubnode/removeFromSupernode
94-
UIView *superview = self.superview;
95-
96-
// If superview's node is different from supernode's view, fix it by setting supernode to the new superview's node. Got that?
97-
if (!superview)
98-
[_node __setSupernode:nil];
99-
else if (superview != _node.supernode.view)
100-
[_node __setSupernode:superview.asyncdisplaykit_node];
121+
ASDisplayNode *supernode = _node.supernode;
122+
ASDisplayNodeAssert(!supernode.isLayerBacked, @"Shouldn't be possible for superview's node to be layer-backed.");
123+
124+
if (supernode) {
125+
ASDisplayNodeAssertTrue(_node.nodeLoaded);
126+
UIView *superview = self.superview;
127+
BOOL supernodeLoaded = supernode.nodeLoaded;
128+
BOOL needsSupernodeRemoval = NO;
129+
130+
if (superview) {
131+
// If our new superview is not the same as the supernode's view, or the supernode has no view, disconnect.
132+
needsSupernodeRemoval = (!supernodeLoaded || supernode.view != superview);
133+
} else {
134+
// If supernode is loaded but our superview is nil, the user manually removed us, so disconnect supernode.
135+
needsSupernodeRemoval = supernodeLoaded;
136+
}
137+
if (needsSupernodeRemoval) {
138+
// The node will only disconnect from its supernode, not removeFromSuperview, in this condition.
139+
[_node removeFromSupernode];
140+
}
141+
}
101142
}
102143

103144
- (void)setNeedsDisplay

0 commit comments

Comments
 (0)