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

Commit 7c4ee35

Browse files
author
Adlai Holler
authored
Use Native convertRect:, convertPoint: Methods to Handle Nil Cases (#2889)
* When converting rects, points on loaded nodes, call into UIKit/CoreAnimation * Always use view-ish behavior * Improve nil window handling, add documentation
1 parent 5b104c0 commit 7c4ee35

5 files changed

Lines changed: 90 additions & 1 deletion

File tree

AsyncDisplayKit/ASDisplayNode.mm

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1805,6 +1805,21 @@ static inline CATransform3D _calculateTransformFromReferenceToTarget(ASDisplayNo
18051805
- (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node
18061806
{
18071807
ASDisplayNodeAssertThreadAffinity(self);
1808+
1809+
/**
1810+
* When passed node=nil, all methods in this family use the UIView-style
1811+
* behavior – that is, convert from/to window coordinates if there's a window,
1812+
* otherwise return the point untransformed.
1813+
*/
1814+
if (node == nil && self.nodeLoaded) {
1815+
CALayer *layer = self.layer;
1816+
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
1817+
return [layer convertPoint:point fromLayer:window.layer];
1818+
} else {
1819+
return point;
1820+
}
1821+
}
1822+
18081823
// Get root node of the accessible node hierarchy, if node not specified
18091824
node = node ? : ASDisplayNodeUltimateParentOfNode(self);
18101825

@@ -1820,6 +1835,16 @@ - (CGPoint)convertPoint:(CGPoint)point fromNode:(ASDisplayNode *)node
18201835
- (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node
18211836
{
18221837
ASDisplayNodeAssertThreadAffinity(self);
1838+
1839+
if (node == nil && self.nodeLoaded) {
1840+
CALayer *layer = self.layer;
1841+
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
1842+
return [layer convertPoint:point toLayer:window.layer];
1843+
} else {
1844+
return point;
1845+
}
1846+
}
1847+
18231848
// Get root node of the accessible node hierarchy, if node not specified
18241849
node = node ? : ASDisplayNodeUltimateParentOfNode(self);
18251850

@@ -1835,6 +1860,16 @@ - (CGPoint)convertPoint:(CGPoint)point toNode:(ASDisplayNode *)node
18351860
- (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node
18361861
{
18371862
ASDisplayNodeAssertThreadAffinity(self);
1863+
1864+
if (node == nil && self.nodeLoaded) {
1865+
CALayer *layer = self.layer;
1866+
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
1867+
return [layer convertRect:rect fromLayer:window.layer];
1868+
} else {
1869+
return rect;
1870+
}
1871+
}
1872+
18381873
// Get root node of the accessible node hierarchy, if node not specified
18391874
node = node ? : ASDisplayNodeUltimateParentOfNode(self);
18401875

@@ -1850,6 +1885,16 @@ - (CGRect)convertRect:(CGRect)rect fromNode:(ASDisplayNode *)node
18501885
- (CGRect)convertRect:(CGRect)rect toNode:(ASDisplayNode *)node
18511886
{
18521887
ASDisplayNodeAssertThreadAffinity(self);
1888+
1889+
if (node == nil && self.nodeLoaded) {
1890+
CALayer *layer = self.layer;
1891+
if (UIWindow *window = ASFindWindowOfLayer(layer)) {
1892+
return [layer convertRect:rect toLayer:window.layer];
1893+
} else {
1894+
return rect;
1895+
}
1896+
}
1897+
18531898
// Get root node of the accessible node hierarchy, if node not specified
18541899
node = node ? : ASDisplayNodeUltimateParentOfNode(self);
18551900

AsyncDisplayKit/ASDisplayNodeExtras.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ extern ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernode(ASDisplayNode *
125125
*/
126126
extern __kindof ASDisplayNode * _Nullable ASDisplayNodeFindFirstSupernodeOfClass(ASDisplayNode *start, Class c) AS_WARN_UNUSED_RESULT;
127127

128+
/**
129+
* Given a layer, find the window it lives in, if any.
130+
*/
131+
extern UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer) AS_WARN_UNUSED_RESULT;
132+
128133
/**
129134
* Given two nodes, finds their most immediate common parent. Used for geometry conversion methods.
130135
* NOTE: It is an error to try to convert between nodes which do not share a common ancestor. This behavior is

AsyncDisplayKit/ASDisplayNodeExtras.mm

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,21 @@ static inline BOOL _ASDisplayNodeIsAncestorOfDisplayNode(ASDisplayNode *possible
249249
return NO;
250250
}
251251

252+
extern UIWindow * _Nullable ASFindWindowOfLayer(CALayer *layer)
253+
{
254+
while (layer != nil) {
255+
if (UIView *view = ASDynamicCast(layer.delegate, UIView)) {
256+
if ([view isKindOfClass:[UIWindow class]]) {
257+
return (UIWindow *)view;
258+
} else {
259+
return view.window;
260+
}
261+
}
262+
layer = layer.superlayer;
263+
}
264+
return nil;
265+
}
266+
252267
extern ASDisplayNode *ASDisplayNodeFindClosestCommonAncestor(ASDisplayNode *node1, ASDisplayNode *node2)
253268
{
254269
ASDisplayNode *possibleAncestor = node1;

AsyncDisplayKitTests/ASDisplayNodeTests.m

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2278,4 +2278,25 @@ - (void)testThatBackgroundLayoutSpecOrdersSubnodesCorrectly
22782278
XCTAssertLessThan(underlayIndex, overlayIndex);
22792279
}
22802280

2281+
- (void)testThatConvertPointGoesToWindowWhenPassedNil
2282+
{
2283+
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
2284+
ASDisplayNode *node = [[ASDisplayNode alloc] init];
2285+
node.frame = CGRectMake(10, 10, 10, 10);
2286+
[window addSubnode:node];
2287+
CGPoint expectedOrigin = CGPointMake(10, 10);
2288+
ASXCTAssertEqualPoints([node convertPoint:node.bounds.origin toNode:nil], expectedOrigin);
2289+
}
2290+
2291+
- (void)testThatConvertPointGoesToWindowWhenPassedNil_layerBacked
2292+
{
2293+
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
2294+
ASDisplayNode *node = [[ASDisplayNode alloc] init];
2295+
node.layerBacked = YES;
2296+
node.frame = CGRectMake(10, 10, 10, 10);
2297+
[window addSubnode:node];
2298+
CGPoint expectedOrigin = CGPointMake(10, 10);
2299+
ASXCTAssertEqualPoints([node convertPoint:node.bounds.origin toNode:nil], expectedOrigin);
2300+
}
2301+
22812302
@end

Base/ASBaseDefines.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,7 @@
184184
#define ASOVERLOADABLE __attribute__((overloadable))
185185

186186
/// Ensure that class is of certain kind
187-
#define ASDynamicCast(x, c) ((c *) ([x isKindOfClass:[c class]] ? x : nil))
187+
#define ASDynamicCast(x, c) ({ \
188+
id __val = x;\
189+
((c *) ([__val isKindOfClass:[c class]] ? __val : nil));\
190+
})

0 commit comments

Comments
 (0)