Skip to content

Instantly share code, notes, and snippets.

@junping1
Created November 29, 2024 09:06
Show Gist options
  • Save junping1/ffb1ba7ce9c0b097b9f4a00f1339c45d to your computer and use it in GitHub Desktop.
Save junping1/ffb1ba7ce9c0b097b9f4a00f1339c45d to your computer and use it in GitHub Desktop.
Perfect Shadow SwiftUI
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