-
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
Set background color directly to view if not layer backed #1588
Conversation
🚫 CI failed with log |
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.
Makes sense to me to always use a UIColor whenever the node is view-backed now that UIColor has (light & dark) magic!
🚫 CI failed with log |
🚫 CI failed with log |
🚫 CI failed with log |
is layerBacked. Summary: UIColor can now be a dynamic color provider to support capabilities like Dark Mode in applications. UIKit is able to re-render views when a user enters dark mode but this dynamic capabilities is not handled at the layer level. Currently this takes the recommendation in facebookarchive/AsyncDisplayKit#1537 (comment) to set background color on the view directly or layer depending on if the node is layer backed. This change allows UIKit to track these colors and update the UI when the UITraitCollection.userInterfaceStyle changes.
e56ebc0
to
ebc8831
Compare
🚫 CI failed with log |
🚫 CI failed with log |
Dynamic capability is lost during conversion between UIColor and CGColor. Thus a UIColor reference is required in this case |
@@ -727,7 +734,14 @@ - (void)setContentMode:(UIViewContentMode)contentMode | |||
- (UIColor *)backgroundColor | |||
{ | |||
_bridge_prologue_read; | |||
return [UIColor colorWithCGColor:_getFromLayer(backgroundColor)]; | |||
if (_loaded(self)) { | |||
if (_view != nil) { |
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.
Use _flags.layerBacked
to be consistent with other methods?
🚫 CI failed with log |
🚫 CI failed with log |
Podfile.lock
Outdated
@@ -52,4 +52,4 @@ SPEC CHECKSUMS: | |||
|
|||
PODFILE CHECKSUM: 445046ac151568c694ff286684322273f0b597d6 | |||
|
|||
COCOAPODS: 1.6.0 | |||
COCOAPODS: 1.6.1 |
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.
We should not update the cocoapods version with this commit imho
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'll undo this
BOOL shouldApply = ASDisplayNodeShouldApplyBridgedWriteToView(self); | ||
|
||
if (shouldApply) { | ||
BOOL oldOpaque = _layer.opaque; | ||
BOOL oldOpaque; | ||
oldOpaque = _layer.opaque; |
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.
Could you please elaborate why we are making this change here?
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.
The values of opaque differ depending on if it's a view or layer backed.
Tests/ASDisplayNodeTests.mm
Outdated
@@ -1924,16 +1925,11 @@ - (void)checkBackgroundColorOpaqueRelationshipWithViewLoaded:(BOOL)loaded layerB | |||
XCTAssertTrue(node.opaque, @"Node should start opaque"); | |||
XCTAssertTrue(node.layer.opaque, @"Node should start opaque"); | |||
|
|||
node.backgroundColor = [UIColor clearColor]; | |||
node.backgroundColor = [UIColor blackColor]; |
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.
Could you elaborate why this test is failing now?
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 was just for testing to see if clearColor
was changing the interpretation of isOpaque
on CALayer
// Set the background color to the layer as in the UIView bridge we use this value as background color | ||
layer.backgroundColor = backgroundColor; | ||
} | ||
view.backgroundColor = [UIColor colorWithCGColor:backgroundColor]; |
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.
If we set the background color to the view the layer should automatically be updated. Could you double cbeck that this is not the case.
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.
@maicki - We tested this today and surprisingly, the layer is not updated (at least not immediately)
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.
Confirm what Rahul said: the view doesn't propagate the background color immediately which causes many of our tests to fail because they use the layer to render snapshots. For example this test failed because the parent node has a nil
background color by the time the test takes a snapshot using ASSnapshotVerifyNode
.
An alternative approach would be updating ASSnapshotVerifyNode
to use the view if the node is view-backed. But by doing so, we're accepting the risk that there is a time window in which the view and the layer disagree. And this time window is undocumented by UIKit.
Given that now UIColor and UIView has a new dynamic capability, and that we should rely on UIView more to take advantage of this new capability, we should update the framework to set to both the view and the layer and thus eliminate the time window and its associated risks.
} else { | ||
_layer.backgroundColor = newBackgroundCGColor; | ||
_view.backgroundColor = newBackgroundColor; | ||
_layer.backgroundColor = _view.backgroundColor.CGColor; |
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.
Same as below if you update the view I don't think you need to update the layer.
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.
@nguyenhuy and I were debugging and realized we needed to set both of these. UIView seems to be setting the _layer.backgroundColor
to nil
when setting the views background color.
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.
We assume these values are bridged but when debugging it seems like the relationship between view and layer is more complex. We're only hitting this now since we are setting backgroundColor directly on the view itself
75fa029
to
3d2d5d4
Compare
Tests/ASDisplayNodeTests.mm
Outdated
XCTAssertTrue(node.layer.opaque, @"Set background color should not have made this layer not opaque"); | ||
} else { | ||
XCTAssertTrue(node.view.opaque, @"Set background color should not have made this view not opaque"); | ||
} |
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.
Is this necessary? At the beginning the view and the layer should agree with each other?
Nit: indentation is off.
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.
gah 2 spaces
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.
so they don't agree with each other in practice. i'll add a comment here
Tests/ASDisplayNodeTests.mm
Outdated
|
||
node.backgroundColor = [UIColor clearColor]; | ||
// [node.view setNeedsDisplay]; |
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.
Wondering the reason behind having this line in the first place. Would that be because doing this causes the view to sync its states (opaque and background color) to the layer? Even if that's true, I think we still shouldn't rely on it because of what I said in the preview comment.
Plus, we should just remove this line.
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 meant to remove this!
// Set the background color to the layer as in the UIView bridge we use this value as background color | ||
layer.backgroundColor = backgroundColor; | ||
} | ||
view.backgroundColor = [UIColor colorWithCGColor:backgroundColor]; |
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.
Confirm what Rahul said: the view doesn't propagate the background color immediately which causes many of our tests to fail because they use the layer to render snapshots. For example this test failed because the parent node has a nil
background color by the time the test takes a snapshot using ASSnapshotVerifyNode
.
An alternative approach would be updating ASSnapshotVerifyNode
to use the view if the node is view-backed. But by doing so, we're accepting the risk that there is a time window in which the view and the layer disagree. And this time window is undocumented by UIKit.
Given that now UIColor and UIView has a new dynamic capability, and that we should rely on UIView more to take advantage of this new capability, we should update the framework to set to both the view and the layer and thus eliminate the time window and its associated risks.
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.
LGTM!
I'm going to merge this PR once CI passes. We want to get this in before our sync this week so we can test it immediately. @maicki @Adlai-Holler @appleguy Feel free to do post-reviews. @rahul-malik and I will follow up on any of your concerns in new diffs. |
🚫 CI failed with log |
🚫 CI failed with log |
UIColor can now be a dynamic color provider to support capabilities like Dark Mode in applications. UIKit is able to re-render views when a user enters dark mode but this dynamic capabilities is not handled at the layer level.
Currently this takes the recommendation in facebookarchive/AsyncDisplayKit#1537 (comment) to set background color on the view directly or layer depending on if the node is layer backed. This change allows UIKit to track these colors and update the UI when the UITraitCollection.userInterfaceStyle changes.