Created
November 29, 2024 09:06
-
-
Save junping1/ffb1ba7ce9c0b097b9f4a00f1339c45d to your computer and use it in GitHub Desktop.
Perfect Shadow SwiftUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
public struct PrimaryShadowModifier: ViewModifier { | |
private let cornerRadius: CGFloat | |
public init(cornerRadius: CGFloat = .zero) { | |
self.cornerRadius = cornerRadius | |
} | |
public func body(content: Content) -> some View { | |
content | |
.dropShadow(hex: "0E3F7F", opacity: 0.04, x: 0, y: 0, blur: 0, spread: 1, cornerRadius: cornerRadius) | |
.dropShadow(hex: "2A3345", opacity: 0.04, x: 0, y: 1, blur: 1, spread: -0.5, cornerRadius: cornerRadius) | |
.dropShadow(hex: "2A3346", opacity: 0.04, x: 0, y: 3, blur: 3, spread: -1.5, cornerRadius: cornerRadius) | |
.dropShadow(hex: "2A3346", opacity: 0.04, x: 0, y: 6, blur: 6, spread: -3, cornerRadius: cornerRadius) | |
.dropShadow(hex: "0E3F7E", opacity: 0.04, x: 0, y: 12, blur: 12, spread: -6, cornerRadius: cornerRadius) | |
.dropShadow(hex: "0E3F7E", opacity: 0.04, x: 0, y: 24, blur: 24, spread: -12, cornerRadius: cornerRadius) | |
} | |
} | |
public struct DropShadowModifier: ViewModifier { | |
private let color: Color | |
private let x: CGFloat | |
private let y: CGFloat | |
private let blur: CGFloat | |
private let spread: CGFloat | |
private let cornerRadius: CGFloat | |
public init(color: Color, x: CGFloat, y: CGFloat, blur: CGFloat, spread: CGFloat, cornerRadius: CGFloat = .zero) { | |
self.color = color | |
self.x = x | |
self.y = y | |
self.blur = blur | |
self.spread = spread | |
self.cornerRadius = cornerRadius | |
} | |
public func body(content: Content) -> some View { | |
content | |
.background( | |
color | |
.clipShape( | |
RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) | |
) | |
.padding(.horizontal, -spread) | |
.padding(.vertical, -spread) | |
.blur(radius: blur) | |
.offset(x: x, y: y) | |
) | |
// .background( | |
// color | |
// .padding(.horizontal, -spread) | |
// .padding(.vertical, -spread) | |
// .blur(radius: blur) | |
// .offset(x: x, y: y) | |
// ) | |
} | |
} | |
public extension View { | |
func dropShadow(color: Color, x: CGFloat = 0, y: CGFloat = 0, blur: CGFloat = 0, spread: CGFloat = 0) -> some View { | |
modifier(DropShadowModifier(color: color, x: x, y: y, blur: blur, spread: spread)) | |
} | |
func dropShadow(hex: String, opacity: Double = 1.0, x: CGFloat = 0, y: CGFloat = 0, blur: CGFloat = 0, spread: CGFloat = 0) -> some View { | |
modifier(DropShadowModifier(color: Color(hex: hex, opacity: opacity) ?? .clear, x: x, y: y, blur: blur, spread: spread)) | |
} | |
func dropShadow(hex: String, opacity: Double = 1.0, x: CGFloat = 0, y: CGFloat = 0, blur: CGFloat = 0, spread: CGFloat = 0, cornerRadius: CGFloat = .zero) -> some View { | |
modifier(DropShadowModifier(color: Color(hex: hex, opacity: opacity) ?? .clear, x: x, y: y, blur: blur, spread: spread, cornerRadius: cornerRadius)) | |
} | |
func primaryShadow(cornerRadius: CGFloat = .zero) -> some View { | |
modifier(PrimaryShadowModifier(cornerRadius: cornerRadius)) | |
} | |
} | |
extension Color { | |
init?(hex: String, opacity: Double = 1.0) { | |
var hexSanitized = hex.trimmingCharacters(in: .whitespacesAndNewlines) | |
hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "") | |
var rgb: UInt64 = 0 | |
guard Scanner(string: hexSanitized).scanHexInt64(&rgb) else { return nil } | |
let red, green, blue: Double | |
let alpha = opacity | |
switch hexSanitized.count { | |
case 3: | |
(red, green, blue) = ( | |
Double((rgb >> 8) & 0x0F) / 15.0, | |
Double((rgb >> 4) & 0x0F) / 15.0, | |
Double(rgb & 0x0F) / 15.0 | |
) | |
case 6: | |
(red, green, blue) = ( | |
Double((rgb >> 16) & 0xFF) / 255.0, | |
Double((rgb >> 8) & 0xFF) / 255.0, | |
Double(rgb & 0xFF) / 255.0 | |
) | |
case 8: | |
(red, green, blue) = ( | |
Double((rgb >> 24) & 0xFF) / 255.0, | |
Double((rgb >> 16) & 0xFF) / 255.0, | |
Double((rgb >> 8) & 0xFF) / 255.0 | |
) | |
default: | |
return nil | |
} | |
self.init(.sRGB, red: red, green: green, blue: blue, opacity: alpha) | |
} | |
} | |
struct AppTheme: View { | |
var body: some View { | |
VStack(spacing: 44) { | |
Button {} label: { | |
Text("Start") | |
.frame(width: 144, height: 38) | |
} | |
.tint(Color(hex: "#0E3F7E") ?? .clear) | |
.buttonStyle(.borderedProminent) | |
.primaryShadow() | |
Rectangle() | |
.fill(Color.white) | |
.frame(width: 100, height: 100) | |
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) | |
.dropShadow(hex: "0E3F7F", opacity: 0.04, x: 0, y: 0, blur: 0, spread: 1) | |
.dropShadow(hex: "2A3345", opacity: 0.04, x: 0, y: 1, blur: 1, spread: -0.5) | |
.dropShadow(hex: "2A3346", opacity: 0.04, x: 0, y: 3, blur: 3, spread: -1.5) | |
.dropShadow(hex: "2A3346", opacity: 0.04, x: 0, y: 6, blur: 6, spread: -3) | |
.dropShadow(hex: "0E3F7E", opacity: 0.04, x: 0, y: 12, blur: 12, spread: -6) | |
.dropShadow(hex: "0E3F7E", opacity: 0.04, x: 0, y: 24, blur: 24, spread: -12) | |
Rectangle() | |
.fill(Color.white) | |
.frame(width: 100, height: 100) | |
.clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) | |
.primaryShadow() | |
} | |
} | |
} | |
#Preview { | |
AppTheme() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment