-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ASDisplayNode] Provide safeAreaInsets and layoutMargins bridge #685
Changes from all commits
9de217b
f881370
5b47e31
7e9ff4e
727b4ae
fd27ff7
cdbc527
5268f14
1abbebc
701a61d
a97c3bc
40de117
43cf7c2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,7 @@ @implementation ASViewController | |
NSInteger _visibilityDepth; | ||
BOOL _selfConformsToRangeModeProtocol; | ||
BOOL _nodeConformsToRangeModeProtocol; | ||
UIEdgeInsets _fallbackAdditionalSafeAreaInsets; | ||
} | ||
|
||
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil | ||
|
@@ -73,10 +74,14 @@ - (void)_initializeInstance | |
if (_node == nil) { | ||
return; | ||
} | ||
|
||
_node.viewControllerRoot = YES; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is fine for now, especially because it's thread-safe and faster than One edge case I can think of is that a root-of-view-controller node is reused and added to a different node hierarchy. It is rare, but we should document is somehow, and maybe assert in debug mode (e.g if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added an assertion to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
|
||
_selfConformsToRangeModeProtocol = [self conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]; | ||
_nodeConformsToRangeModeProtocol = [_node conformsToProtocol:@protocol(ASRangeControllerUpdateRangeProtocol)]; | ||
_automaticallyAdjustRangeModeBasedOnViewEvents = _selfConformsToRangeModeProtocol || _nodeConformsToRangeModeProtocol; | ||
|
||
_fallbackAdditionalSafeAreaInsets = UIEdgeInsetsZero; | ||
|
||
// In case the node will get loaded | ||
if (_node.nodeLoaded) { | ||
|
@@ -159,6 +164,20 @@ - (void)viewDidLayoutSubviews | |
[_node recursivelyEnsureDisplaySynchronously:YES]; | ||
} | ||
[super viewDidLayoutSubviews]; | ||
|
||
if (!AS_AT_LEAST_IOS11) { | ||
[self _updateNodeFallbackSafeArea]; | ||
} | ||
} | ||
|
||
- (void)_updateNodeFallbackSafeArea | ||
{ | ||
UIEdgeInsets safeArea = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length, 0); | ||
UIEdgeInsets additionalInsets = self.additionalSafeAreaInsets; | ||
|
||
safeArea = ASConcatInsets(safeArea, additionalInsets); | ||
|
||
_node.fallbackSafeAreaInsets = safeArea; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
|
||
ASVisibilityDidMoveToParentViewController; | ||
|
@@ -264,6 +283,25 @@ - (ASInterfaceState)interfaceState | |
return _node.interfaceState; | ||
} | ||
|
||
- (UIEdgeInsets)additionalSafeAreaInsets | ||
{ | ||
if (AS_AVAILABLE_IOS(11.0)) { | ||
return super.additionalSafeAreaInsets; | ||
} | ||
|
||
return _fallbackAdditionalSafeAreaInsets; | ||
} | ||
|
||
- (void)setAdditionalSafeAreaInsets:(UIEdgeInsets)additionalSafeAreaInsets | ||
{ | ||
if (AS_AVAILABLE_IOS(11.0)) { | ||
[super setAdditionalSafeAreaInsets:additionalSafeAreaInsets]; | ||
} else { | ||
_fallbackAdditionalSafeAreaInsets = additionalSafeAreaInsets; | ||
[self _updateNodeFallbackSafeArea]; | ||
} | ||
} | ||
|
||
#pragma mark - ASTraitEnvironment | ||
|
||
- (ASPrimitiveTraitCollection)primitiveTraitCollectionForUITraitCollection:(UITraitCollection *)traitCollection | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -241,6 +241,25 @@ __unused static NSString * _Nonnull NSStringFromASHierarchyStateChange(ASHierarc | |
*/ | ||
- (BOOL)shouldScheduleDisplayWithNewInterfaceState:(ASInterfaceState)newInterfaceState; | ||
|
||
/** | ||
* @abstract safeAreaInsets will fallback to this value if the corresponding UIKit property is not available | ||
* (due to an old iOS version). | ||
* | ||
* @discussion This should be set by the owning view controller based on it's layout guides. | ||
* If this is not a view controllet's node the value will be calculated automatically by the parent node. | ||
*/ | ||
@property (nonatomic, assign) UIEdgeInsets fallbackSafeAreaInsets; | ||
|
||
/** | ||
* @abstract Indicates if this node is a view controller's root node. Defaults to NO. | ||
* | ||
* @discussion Set to YES in -[ASViewController initWithNode:]. | ||
* | ||
* YES here only means that this node is used as an ASViewController node. It doesn't mean that this node is a root of | ||
* ASDisplayNode hierarchy, e.g. when its view controller is parented by another ASViewController. | ||
*/ | ||
@property (nonatomic, assign, getter=isViewControllerRoot) BOOL viewControllerRoot; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call on making this property private :) |
||
|
||
@end | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is called by
__layout
which means it runs on every frame and must be treated as a hot path. I haven't profiled to see how fast/slow it is, but I think it's worthy to try to add some straightforward optimizations right from the beginning.One optimization I can think of is that, since fallback safe area insets are only used for backward compatibility, we can bail early/skip a child whenever possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a shortcut that skips a child if it is a view-backed node and we run on iOS 11. And
-[ASDisplayNode setFallbackSafeAreaInsets:]
returns early when the insets don't change.Although we could try to skip calculations for the children that use fallback safe area insets but don't really need to have them updated (e.g. neither the child's frame, the parent's frame nor the parent's safe area wasn't updated), I doubt that it would save more CPU time than would spend running the checks.
The other thing I'm thinking about is that on iOS 11 we can disable fallback logic completely if we avoid supporting the safe area for the layer-backed nodes. But I'm not sure if we should do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the note. I think this implementation and your trade-offs are reasonable. Let's roll with this.