-
Notifications
You must be signed in to change notification settings - Fork 59
Add ShadowEffect support #779
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
Changes from all commits
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 |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| // | ||
| // ShadowEffectUITests.swift | ||
| // OpenSwiftUIUITests | ||
|
|
||
| import Testing | ||
| import SnapshotTesting | ||
|
|
||
| @MainActor | ||
| @Suite(.snapshots(record: .never, diffTool: diffTool)) | ||
| struct ShadowEffectUITests { | ||
| @Test | ||
| func shadowDefault() { | ||
| struct ContentView: View { | ||
| var body: some View { | ||
| Color.blue | ||
| .frame(width: 100, height: 100) | ||
| .shadow(radius: 10) | ||
| } | ||
| } | ||
| openSwiftUIAssertSnapshot(of: ContentView(), drawHierarchyInKeyWindow: true) | ||
| } | ||
|
|
||
| @Test | ||
| func shadowCustomColor() { | ||
| struct ContentView: View { | ||
| var body: some View { | ||
| Color.blue | ||
| .frame(width: 100, height: 100) | ||
| .shadow(color: .red, radius: 10) | ||
| } | ||
| } | ||
| openSwiftUIAssertSnapshot(of: ContentView(), drawHierarchyInKeyWindow: true) | ||
| } | ||
|
|
||
| @Test | ||
| func shadowOffset() { | ||
| struct ContentView: View { | ||
| var body: some View { | ||
| Color.blue | ||
| .frame(width: 100, height: 100) | ||
| .shadow(color: .black, radius: 5, x: 10, y: 10) | ||
| } | ||
| } | ||
| openSwiftUIAssertSnapshot(of: ContentView(), drawHierarchyInKeyWindow: true) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| // | ||
| // ShadowEffect.swift | ||
| // OpenSwiftUICore | ||
| // | ||
| // Audited for 6.5.4 | ||
| // Status: Complete | ||
|
|
||
| public import Foundation | ||
|
|
||
| // MARK: - _ShadowEffect | ||
|
|
||
| @available(OpenSwiftUI_v1_0, *) | ||
| @frozen | ||
| public struct _ShadowEffect: EnvironmentalModifier, Equatable { | ||
| public var color: Color | ||
|
|
||
| public var radius: CGFloat | ||
|
|
||
| public var offset: CGSize | ||
|
|
||
| @inlinable | ||
| public init(color: Color, radius: CGFloat, offset: CGSize) { | ||
| self.color = color | ||
| self.radius = radius | ||
| self.offset = offset | ||
| } | ||
|
|
||
| public func resolve(in environment: EnvironmentValues) -> _ShadowEffect._Resolved { | ||
| _Resolved(style: ResolvedShadowStyle( | ||
| color: color.resolve(in: environment), | ||
| radius: radius, | ||
| offset: offset | ||
| )) | ||
| } | ||
|
|
||
| @available(OpenSwiftUI_v4_0, *) | ||
| public static var _requiresMainThread: Bool { | ||
| false | ||
| } | ||
|
|
||
| @usableFromInline | ||
| internal var _requiresMainThread: Bool { | ||
| false | ||
| } | ||
|
|
||
| // MARK: - _Resolved | ||
|
|
||
| public struct _Resolved: RendererEffect { | ||
| package var style: ResolvedShadowStyle | ||
|
|
||
| public typealias AnimatableData = AnimatablePair<AnimatablePair<Float, AnimatablePair<Float, AnimatablePair<Float, Float>>>, AnimatablePair<CGFloat, CGSize.AnimatableData>> | ||
|
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.
Severity: low 🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage. |
||
|
|
||
| public var animatableData: AnimatableData { | ||
| get { style.animatableData } | ||
| set { style.animatableData = newValue } | ||
| } | ||
|
|
||
| package func effectValue(size: CGSize) -> DisplayList.Effect { | ||
| .filter(.shadow(style)) | ||
| } | ||
|
|
||
| public typealias Body = Never | ||
| } | ||
|
|
||
| nonisolated public static func == (a: _ShadowEffect, b: _ShadowEffect) -> Bool { | ||
| a.color == b.color && a.radius == b.radius && a.offset == b.offset | ||
| } | ||
|
|
||
| public typealias Body = Never | ||
|
|
||
| public typealias ResolvedModifier = _ShadowEffect._Resolved | ||
| } | ||
|
|
||
| @available(*, unavailable) | ||
| extension _ShadowEffect: Sendable {} | ||
|
|
||
| @available(*, unavailable) | ||
| extension _ShadowEffect._Resolved: Sendable {} | ||
|
|
||
| // MARK: - View + shadow | ||
|
|
||
| @available(OpenSwiftUI_v1_0, *) | ||
| extension View { | ||
|
|
||
| /// Adds a shadow to this view. | ||
| /// | ||
| /// Use this modifier to add a shadow of a specified color behind a view. | ||
| /// You can offset the shadow from its view independently in the horizontal | ||
| /// and vertical dimensions using the `x` and `y` parameters. You can also | ||
| /// blur the edges of the shadow using the `radius` parameter. Use a | ||
| /// radius of zero to create a sharp shadow. Larger radius values produce | ||
| /// softer shadows. | ||
| /// | ||
| /// The example below creates a grid of boxes with varying offsets and blur. | ||
| /// Each box displays its radius and offset values for reference. | ||
| /// | ||
| /// struct Shadow: View { | ||
| /// let steps = [0, 5, 10] | ||
| /// | ||
| /// var body: some View { | ||
| /// VStack(spacing: 50) { | ||
| /// ForEach(steps, id: \.self) { offset in | ||
| /// HStack(spacing: 50) { | ||
| /// ForEach(steps, id: \.self) { radius in | ||
| /// Color.blue | ||
| /// .shadow( | ||
| /// color: .primary, | ||
| /// radius: CGFloat(radius), | ||
| /// x: CGFloat(offset), y: CGFloat(offset)) | ||
| /// .overlay { | ||
| /// VStack { | ||
| /// Text("\(radius)") | ||
| /// Text("(\(offset), \(offset))") | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// } | ||
| /// | ||
| ///  | ||
| /// | ||
| /// The example above uses ``Color/primary`` as the color to make the | ||
| /// shadow easy to see for the purpose of illustration. In practice, | ||
| /// you might prefer something more subtle, like ``Color/gray-8j2b``. | ||
| /// If you don't specify a color, the method uses a semi-transparent | ||
| /// black. | ||
| /// | ||
| /// - Parameters: | ||
| /// - color: The shadow's color. | ||
| /// - radius: A measure of how much to blur the shadow. Larger values | ||
| /// result in more blur. | ||
| /// - x: An amount to offset the shadow horizontally from the view. | ||
| /// - y: An amount to offset the shadow vertically from the view. | ||
| /// | ||
| /// - Returns: A view that adds a shadow to this view. | ||
| @inlinable | ||
| nonisolated public func shadow( | ||
| color: Color = Color(.sRGBLinear, white: 0, opacity: 0.33), | ||
| radius: CGFloat, | ||
|
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. The public Severity: medium 🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage. |
||
| x: CGFloat = 0, | ||
| y: CGFloat = 0 | ||
| ) -> some View { | ||
| return modifier( | ||
| _ShadowEffect( | ||
| color: color, | ||
| radius: radius, | ||
| offset: CGSize(width: x, height: y) | ||
| ) | ||
| ) | ||
| } | ||
| } | ||
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.
EnvironmentalModifier._requiresMainThreadis available startingOpenSwiftUI_v3_0, but this override is only available fromOpenSwiftUI_v4_0, so on v3 the defaulttruemay still apply and prevent async updates. Also, the instance_requiresMainThreadproperty doesn’t appear to be referenced elsewhere, so it might be dead/unintentional.Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.