[Paywalls] Use CALayer-backed shadows and refactor Shape.swift#4630
Conversation
1 build increased size
Paywalls 1.0 (1)
|
| Item | Install Size Change |
|---|---|
| DYLD.String Table | ⬆️ 40.2 kB |
| Code Signature | ⬆️ 2.9 kB |
| DYLD.Exports | ⬆️ 2.4 kB |
| 🗑 RevenueCatUI.BorderRoundedCornerShape | ⬇️ -511 B |
| Other | ⬆️ 69.2 kB |
🛸 Powered by Emerge Tools
Comment trigger: Size diff threshold of 100.00kB exceeded
| } | ||
| shape: style.shape, | ||
| shadow: style.shadow, | ||
| background: style.backgroundStyle) |
There was a problem hiding this comment.
The border already depended on the shape, and now the shadow does as well, so we include it in the shape modifier.
Since the shape modifier already does shape clipping of the background, and requires the backgroundStyle modifier to be applied right before, it makes sense to move it inside the shape modifier as well so we can more easily track the interactions between all the pieces.
| #if PAYWALL_COMPONENTS | ||
|
|
||
| enum BackgroundStyle { | ||
| enum BackgroundStyle: Hashable { |
There was a problem hiding this comment.
Added a bunch of Hashables to be able to programmatically create previews of all combinations using ForEach.
| // ShadowUIView tries to work around these limitations by rendering the shadow in a backing UIView, | ||
| // and using a Shape to mask off the inner part of the shadow. | ||
| @available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) | ||
| private struct LayerShadowView: UIViewRepresentable { |
There was a problem hiding this comment.
This is the main addition of the PR and the way we fix SwiftUI shadows by using a CALayer shadow instead. Documented in code as it feels like important context.
The main API change here is the .shadow modifier now requires a Shape that's used to cut out the inner part of the shadow.
| .backgroundStyle(background) | ||
| // We want to clip only in case there is a non-Rectangle shape | ||
| // or if there's a border, otherwise we let the background color | ||
| // extend behind the safe areas |
There was a problem hiding this comment.
I noticed this when it changed in an Emerge snapshot.
When the shape is a rectangle without rounded corners and there is no border, if we don't clip the background, it can extend behind the safe areas. This might be useful for the root view and for the sticky footer view so it felt important to keep the behavior and document it.
| self | ||
| .border(color, width: width) | ||
| } | ||
| func effectiveRectangleShape(radiusInfo: RadiusInfo?) -> AnyInsettableShape? { |
There was a problem hiding this comment.
Greatly simplified the clipping and overlaying shape by getting rid of SingleRoundedCornerShape and just using BackportedUnevenRoundedRectangle as a drop-in replacement for UnevenRoundedRectangle
| VStack { | ||
| ForEach(shapes, id: \.self) { shape in | ||
| ForEach(borders, id: \.self) { border in | ||
| ForEach(shadows, id: \.self) { shadow in |
There was a problem hiding this comment.
Check the Emerge snapshots for all the new previews :)
0377dff to
8abcd93
Compare

Motivation
By default shadows in SwiftUI are applied to children views as well, so we implemented a workaround by using
.compositingGroup().However when the background color of a view with a shadow has less than 100% opacity, the
.compositingGroup()workaround no longer works. Additionally, the shadow inherits the opacity of the view which is less than ideal.Description
In this PR, we replace SwiftUI shadows with a CALayer-backed solution. Because this solution requires knowing the shape of the view, I took the opportunity to refactor the Shape modifier code.
I didn't modify any current SwiftUI previews so make sure nothing broke. The only changes were minor rendering differences in the shadows which are not perceptible with your naked eye, and a change in how a shadow applied to a
Textview renders, as it now applies to its bounding rectangle and not to the text itself. Fortunately, we don't currently support shadows in text.