-
Notifications
You must be signed in to change notification settings - Fork 24.5k
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
feat: linear gradient ios #45434
feat: linear gradient ios #45434
Conversation
Base commit: 2eb7bcb |
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.
There are a lot of shared files with this impl and the android one in #45433. I did not review those files since @NickGerleman added some suggestions over there. In general I think we should ship the Android one first since it has all of that discussion for those files, and update this PR to reflect those changes (or just make it so we do not have 2 separate things trying to add the same logic)
I did review the iOS specifics and left a few questions. Thanks for doing this btw, its awesome to see!
Gonna request changes just for the reminder to update this PR with the shared code situation mentioned above
@@ -228,6 +229,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & | |||
self.backgroundColor = RCTUIColorFromSharedColor(newViewProps.backgroundColor); | |||
needsInvalidateLayer = YES; | |||
} | |||
|
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.
nit: remove extra space
@@ -413,6 +420,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & | |||
_props = std::static_pointer_cast<const ViewProps>(props); | |||
} | |||
|
|||
|
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, remove extra space
@@ -761,7 +769,7 @@ - (void)invalidateLayer | |||
self.layer.opacity *= primitive.amount; | |||
} | |||
} | |||
|
|||
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
for (const auto& colorStop : gradientPrimitive.colorStops) { | ||
if (colorStop.position.has_value()) { | ||
auto location = @(colorStop.position.value()); | ||
UIColor* color = RCTUIColorFromSharedColor(colorStop.color); | ||
[colors addObject:(id) color.CGColor]; | ||
[locations addObject:location]; | ||
} | ||
} |
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.
will this just not add some colors if they do not have a position? in what cases would that happen? i.e. what cases do we have a color with an empty position
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.
what cases do we have a color with an empty position
That should not happen. In view config processors we make sure that position is always set. We set default position also here if user doesn't pass a position.
|
||
// So that border layer is always above gradient layer | ||
gradientLayer.zPosition = _borderLayer.zPosition; | ||
[self.layer insertSublayer:gradientLayer atIndex:0]; |
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 there a reason this is at index 0? The z position should take care of that dimension for the most part. If you want gradients defined first in the array to be layered on top of the latter ones, maybe consider iterating over the gradients in reverse order so that you can just append to the end 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.
Updated this to below. Tried to explain the approach here in a little bit longer answer
// So that border layer is always above gradient layer
gradientLayer.zPosition = _borderLayer.zPosition - 1;
[self.layer addSublayer:gradientLayer];
if (useCoreAnimationBorderRendering) { | ||
gradientLayer.borderWidth = (CGFloat)borderMetrics.borderWidths.left; | ||
CGColorRef borderColor = RCTCreateCGColorRefFromSharedColor(borderMetrics.borderColors.left); | ||
gradientLayer.borderColor = borderColor; | ||
CGColorRelease(borderColor); | ||
gradientLayer.cornerRadius = (CGFloat)borderMetrics.borderRadii.topLeft; | ||
gradientLayer.cornerCurve = CornerCurveFromBorderCurve(borderMetrics.borderCurves.topLeft); | ||
gradientLayer.shouldRasterize = YES; | ||
gradientLayer.drawsAsynchronously = YES; | ||
} else { | ||
CAShapeLayer* maskLayer = [CAShapeLayer layer]; | ||
CGPathRef path = RCTPathCreateWithRoundedRect(self.bounds, RCTGetCornerInsets( | ||
RCTCornerRadiiFromBorderRadii(borderMetrics.borderRadii), | ||
UIEdgeInsetsZero), nil); | ||
maskLayer.path = path; | ||
CGPathRelease(path); | ||
gradientLayer.mask = maskLayer; | ||
} | ||
[_gradientLayers addObject:gradientLayer]; |
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 am a bit confused why this layer has a border. Borders will go over the background, so as long as this layer is under the border layer it should be fine. Is this more about how the color is distributed when there is a border that pushes the content inward?
5fbec53
to
11fdf1b
Compare
Hi @joevilches thanks for review. I have rebased this against the latest main that has android changes merged. Will try to clarify the above questions here. Let me know if there is a better way to achieve this. Based on how current border style works there are two cases:
Now when we append gradient layers to root view layer, it should support above two cases for borders to work correctly with them.
Added border with linear gradient examples here. AsideAnother approach would be to draw gradients with Core graphics in border layer's image instead of using CA gradient layers for non uniform border styles somewhere here. But that would be CPU intensive instead of GPU. That would be cleaner but might not be a good tradeoff. |
@@ -831,6 +838,56 @@ - (void)invalidateLayer | |||
} | |||
|
|||
[_boxShadowLayer removeFromSuperlayer]; |
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 might be result of a merge that I committed recently, but can you put your code above this line so box shadow logic is together
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.
my bad i missed that, fixed.
@intergalacticspacehighway thanks! this looks good. One thing I just thought of is that I think this breaks with non uniform borders and a background color set. Since the image that we set on the border layer just fills with the background color, and that covers this gradient layers, we run into and issue where we no longer see the gradient. That is honestly a problem with how the border layer is drawn - it should not fill with background color, it should just leave that empty I think, but there seems to be some reasoning why. I do not think the burden is on you to fix that and for users the fix is just don't set background color. But just calling that out |
@joevilches has imported this pull request. If you are a Meta employee, you can view this diff on Phabricator. |
@joevilches yeah I imagined this issue, i honestly thought about it late at night and forgot to add in description sorry 😅. But I agree that the solution involves separating border and background layer drawings. Users can simply choose not to add background color for gradients, not sure if there would be such a usecase anyway. |
@joevilches merged this pull request in b99675d. |
This pull request was successfully merged by @intergalacticspacehighway in b99675d When will my fix make it into a release? | How to file a pick request? |
Summary:
background
prop that supports CSS's linear gradient. Later this can be extended to support various other gradients and possibly CSS's background image (less motivation as better solutions exists for image)CAGradientlayer
to draw Linear Gradient layers. So it is GPU optimised under the hood.LinearGradient
, so it can support Animated libraries.Changelog:
[IOS] [ADDED] - linear gradient
Test Plan:
processBackground-test.js
for supported syntax testcases.Although the PR is tested well but open to any changes/feedback on the approach taken.
Android PR - #45433. Separated the PRs to keep it easier to review. Both PRs can be reviewed individually.